import io import requests import json import re REQUEST_TIMEOUT = 10 def post(_args): ?args = { ??"board": None, ??"thread": None, ??"options": None, ??"name": None, ??"subject": None, ??"comment": None, ??"file": None, ??"filename": None, ??"delpass": None, ??"passid": None ?} ? ?args.update(_args) ? ?if not args["board"]: ??raise PostInvalidError("No board specified") ? ?if not (args["subject"] or args["comment"] or args["file"]): ??raise PostInvalidError("No subject, comment or file specified") ? ?if not args["passid"]: ??raise PostInvalidError("No 4chan pass id specified") ? ?if args["file"] and not isinstance(args["file"], io.IOBase): ??args["file"] = (open(args["file"], "rb") if args["file"] else None) ? ?try: ??request = requests.request( ???method = "post", ???url = ("https://sys.4chan.org/" + args["board"] + "/imgboard.php"), ???timeout = REQUEST_TIMEOUT, ???data = { ????"mode": "regist", ????"resto": (str(args["thread"]) if args["thread"] else ""), ????"name": (args["name"] if args["name"] else ""), ????"email": (args["options"] if args["options"] else ""), ????"sub": (args["subject"] if args["subject"] else ""), ????"com": (args["comment"] if args["comment"] else ""), ????"pwd": (args["delpass"] if args["delpass"] else ""), ???}, ???files = { ????"upfile": ( ?????(args["filename"] if args["filename"] else "file"), ?????args["file"] ????) if args["file"] else () ???}, ???cookies = { ????"pass_enabled": "1", ????"pass_id": args["passid"] ???} ??) ?except requests.exceptions.RequestException as exception: ??raise PostFailedError from exception ? ?postedno = re.search(r"", request.text) ?postedno = int(postedno.group(1)) if postedno else None ? ?if not postedno: ??errmsg = re.search(r"(.+?)", request.text) ??errmsg = errmsg.group(1) if errmsg else "(no error message found)" ?? ??errmsg = re.sub(r"(
)+", r" ", errmsg) ??errmsg = re.sub(r"<.*?>", r"", errmsg) ??errmsg = re.sub(r"^Error: ", r"", errmsg) ?? ??raise PostRejectedError(errmsg) ? ?return postedno def get(_args): ?args = { ??"type": None, ??"board": None, ??"page": None, ??"thread": None ?} ? ?args.update(_args) ? ?url = "https://a.4cdn.org" ? ?if not args["type"] in ["thread", "index", "catalog", "threads", "archive", "boards"]: ??raise GetInvalidError("Invalid type") ? ?if args["type"] == "boards": ??url = (url + "/boards.json") ? ?if args["type"] == "thread": ??if not (args["board"] and args["thread"]): ???raise GetInvalidError("Type 'thread' requires args 'board' and 'thread'") ?? ??url = (url + "/" + args["board"] + "/thread/" + str(args["thread"]) + ".json") ? ?if args["type"] == "index": ??if not (args["board"] and args["page"]): ???raise GetInvalidError("Type 'index' requires args 'board' and 'page'") ?? ??url = (url + "/" + args["board"] + "/" + str(args["page"]) + ".json") ? ?if args["type"] == "catalog": ??if not args["board"]: ???raise GetInvalidError("Type 'catalog' requires args 'board'") ?? ??url = (url + "/" + args["board"] + "/catalog.json") ? ?if args["type"] == "threads": ??if not args["board"]: ???raise GetInvalidError("Type 'threads' requires args 'board'") ?? ??url = (url + "/" + args["board"] + "/threads.json") ? ?if args["type"] == "archive": ??if not args["board"]: ???raise GetInvalidError("Type 'archive' requires args 'board'") ?? ??url = (url + "/" + args["board"] + "/archive.json") ? ?try: ??request = requests.request( ???method = "get", ???url = url, ???timeout = REQUEST_TIMEOUT ??) ?except requests.exceptions.RequestException as exception: ??raise GetFailedError(str(exception)) ? ?if request.status_code >= 400: ??errmsg = (str(request.status_code) + " " + request.reason) ?? ??if request.status_code == 404: ???raise GetNotFoundError(errmsg) ??else: ???raise GetFailedError(errmsg) ? ?data = json.loads(request.text) ? ?if data == []: ??raise GetFailedError ? ?return data class YotsubaError(Exception): ?pass class PostError(YotsubaError): ?pass class PostInvalidError(PostError): ?pass class PostFailedError(PostError): ?pass class PostRejectedError(PostError): ?pass class GetError(YotsubaError): ?pass class GetInvalidError(GetError): ?pass class GetFailedError(GetError): ?pass class GetNotFoundError(GetError): ?pass