post version 0, zero debugging yet
technically I'm vibecoding this and I'm the LLM here
This commit is contained in:
		
							parent
							
								
									92d36ddc7e
								
							
						
					
					
						commit
						c0b4d8692b
					
				
							
								
								
									
										125
									
								
								patchodon.py
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								patchodon.py
									
									
									
									
									
								
							|  | @ -3,9 +3,118 @@ | |||
| import sys, os | ||||
| import argparse | ||||
| import requests | ||||
| import hashlib | ||||
| import time | ||||
| 
 | ||||
| # NOTES: html2text: html2text | ||||
| # the replies are listed by context, should be link-listed to avoid issues, | ||||
| # should specify next hash to provide some kind of a filter | ||||
| # visibility public+unlisted, all unlisted, all private, all direct | ||||
| 
 | ||||
| VERSION = "0.1.0" | ||||
| 
 | ||||
| DPASTE_URL = "https://dpaste.com" | ||||
| 
 | ||||
| STATUS_LENGTH_LIMIT = 400 | ||||
| 
 | ||||
| 
 | ||||
| def trace(x): | ||||
|     sys.stderr.write(sys.argv[0] + ": " + x + "\n") | ||||
| 
 | ||||
| 
 | ||||
| def api_token(args): | ||||
|     if args.debug_api_token: | ||||
|         return args.debug_api_token | ||||
|     if args.env_api_token: | ||||
|         return os.environ["PATCHODON_API_TOKEN"] | ||||
|     raise ("API token not specified") | ||||
| 
 | ||||
| 
 | ||||
| def do_post_status(args, body, parent=None, optional=None): | ||||
|     if len(body) > STATUS_LENGTH_LIMIT: | ||||
|         raise ("required status body too long") | ||||
| 
 | ||||
|     if not args.instance_url: | ||||
|         raise ("mastodon instance not specified") | ||||
| 
 | ||||
|     token = api_token(args) | ||||
| 
 | ||||
|     headers = {"Authorization": f"Bearer {token}"} | ||||
|     st = body + ( | ||||
|         "\n" + optional[0 : (STATUS_LENGTH_LIMIT - len(body) - 1)] | ||||
|         if optional | ||||
|         else "" | ||||
|     ) | ||||
|     data = {status: st, visibility: "unlisted"} | ||||
|     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 | ||||
| 
 | ||||
| 
 | ||||
| def do_pastebin_file(file): | ||||
|     # DPASTE API USE RULES: | ||||
|     # - user-agent must be set properly | ||||
|     # - 1 second between requests | ||||
|     trace(f"pasting {file}...") | ||||
|     r = requests.post( | ||||
|         DPASTE_URL + "/api/v2/", | ||||
|         data={ | ||||
|             "contents": open(file, "r").read(), | ||||
|             "syntax": "diff", | ||||
|             "title": os.path.basename(file), | ||||
|             "expiry_days": 30, | ||||
|         }, | ||||
|         headers={"User-agent": f"patchodon v{VERSION}"}, | ||||
|     ) | ||||
|     if r.status_code != 201: | ||||
|         raise (f"dpaste POST failed for `{file}'") | ||||
|     sleep(1.1) | ||||
|     return r.headers["location"] + ".txt" | ||||
| 
 | ||||
| 
 | ||||
| def split_off_diff(s): | ||||
|     return s.split("\ndiff --git ")[0] | ||||
| 
 | ||||
| 
 | ||||
| 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")) | ||||
|     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) | ||||
|     trace("posting the header...") | ||||
|     parent_post_id, url = do_post_status( | ||||
|         args, | ||||
|         args.subject | ||||
|         + f"\n[patchodon hash {full_hash} shorts {short_hashes.join(' ')}]", | ||||
|     ) | ||||
|     for fn, pst, hsh, series in zip( | ||||
|         files, paste_raw_urls, hashes, range(n_patches) | ||||
|     ): | ||||
|         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", | ||||
|             parent=parent_post_id, | ||||
|             optional=split_off_diff(open(fn, "r").read()), | ||||
|         ) | ||||
|     print(url) | ||||
| 
 | ||||
| 
 | ||||
| def do_get(args): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     ap = argparse.ArgumentParser( | ||||
|  | @ -38,7 +147,13 @@ def main(): | |||
|             "-i", "--instance-url", help="mastodon instance URL to post to" | ||||
|         ) | ||||
|         post.add_argument( | ||||
|             "-r", "--recipient", help="recipient to tag in the initial post" | ||||
|             "-s", | ||||
|             "--subject", | ||||
|             default="", | ||||
|             help=( | ||||
|                 "start of the initial post (e.g. to @tag someone and name the" | ||||
|                 " project and topic)" | ||||
|             ), | ||||
|         ) | ||||
|         post.add_argument( | ||||
|             "patchfile", | ||||
|  | @ -93,7 +208,13 @@ def main(): | |||
|         ), | ||||
|     ) | ||||
|     args = ap.parse_args() | ||||
|     print(args) | ||||
| 
 | ||||
|     if args.command == "post": | ||||
|         do_post(args) | ||||
|     elif args.command == "get": | ||||
|         do_get(args) | ||||
|     else: | ||||
|         raise ("fatal: args borked") | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue