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