summaryrefslogtreecommitdiffstats
path: root/tools/site/upload_file.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/site/upload_file.py')
-rw-r--r--tools/site/upload_file.py132
1 files changed, 132 insertions, 0 deletions
diff --git a/tools/site/upload_file.py b/tools/site/upload_file.py
new file mode 100644
index 0000000..8d347ba
--- /dev/null
+++ b/tools/site/upload_file.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+"""upload_file.py — upload an image to open-iov.org as WikiBot.
+
+Usage:
+ upload_file.py --file PATH --name "File:Example.png" --text "wikitext description"
+"""
+
+import argparse
+import http.cookiejar
+import json
+import mimetypes
+import os
+import sys
+import urllib.parse
+import urllib.request
+import uuid
+
+API = "https://open-iov.org/api.php"
+ENVFILE = os.path.expanduser("~/.config/open-iov/wiki-bot.env")
+UA = "OpenIOV-WikiBot/1.0"
+
+
+def load_creds():
+ user = os.environ.get("WIKI_BOT_USER")
+ password = os.environ.get("WIKI_BOT_PASS")
+ if not (user and password) and os.path.exists(ENVFILE):
+ with open(ENVFILE) as f:
+ for line in f:
+ line = line.strip()
+ if line.startswith("WIKI_BOT_USER="):
+ user = line.split("=", 1)[1]
+ elif line.startswith("WIKI_BOT_PASS="):
+ password = line.split("=", 1)[1]
+ if not (user and password):
+ sys.exit("no credentials")
+ return user, password
+
+
+class Multipart:
+ def __init__(self):
+ self.boundary = uuid.uuid4().hex
+ self.parts = []
+
+ def field(self, name, value):
+ self.parts.append(
+ (
+ f"--{self.boundary}\r\n"
+ f'Content-Disposition: form-data; name="{name}"\r\n\r\n'
+ f"{value}\r\n"
+ ).encode()
+ )
+
+ def file(self, name, filename, data, ctype):
+ self.parts.append(
+ (
+ f"--{self.boundary}\r\n"
+ f'Content-Disposition: form-data; name="{name}"; filename="{filename}"\r\n'
+ f"Content-Type: {ctype}\r\n\r\n"
+ ).encode()
+ + data
+ + b"\r\n"
+ )
+
+ def body(self):
+ return b"".join(self.parts) + f"--{self.boundary}--\r\n".encode()
+
+ def content_type(self):
+ return f"multipart/form-data; boundary={self.boundary}"
+
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument("--file", required=True)
+ ap.add_argument("--name", required=True, help='wiki file title, e.g. "File:Example.png"')
+ ap.add_argument("--text", default="")
+ ap.add_argument("--summary", default="Upload image from tracked analysis")
+ args = ap.parse_args()
+
+ user, password = load_creds()
+ cj = http.cookiejar.CookieJar()
+ opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
+
+ def api(params, post=False):
+ params.setdefault("format", "json")
+ qs = urllib.parse.urlencode(params)
+ if post:
+ req = urllib.request.Request(API, data=qs.encode(), headers={"User-Agent": UA})
+ else:
+ req = urllib.request.Request(API + "?" + qs, headers={"User-Agent": UA})
+ with opener.open(req, timeout=60) as r:
+ return json.loads(r.read())
+
+ tok = api({"action": "query", "meta": "tokens", "type": "login"})["query"]["tokens"]["logintoken"]
+ res = api({"action": "login", "lgname": user, "lgpassword": password, "lgtoken": tok}, post=True)
+ if res.get("login", {}).get("result") != "Success":
+ sys.exit(f"login failed: {res}")
+ csrf = api({"action": "query", "meta": "tokens", "type": "csrf"})["query"]["tokens"]["csrftoken"]
+
+ with open(args.file, "rb") as f:
+ data = f.read()
+ ctype = mimetypes.guess_type(args.file)[0] or "application/octet-stream"
+ mp = Multipart()
+ mp.field("action", "upload")
+ mp.field("format", "json")
+ mp.field("token", csrf)
+ mp.field("filename", args.name)
+ mp.field("comment", args.summary)
+ mp.field("text", args.text)
+ mp.field("ignorewarnings", "1")
+ mp.file("file", os.path.basename(args.file), data, ctype)
+ req = urllib.request.Request(
+ API,
+ data=mp.body(),
+ headers={"User-Agent": UA, "Content-Type": mp.content_type()},
+ )
+ with opener.open(req, timeout=120) as r:
+ raw = r.read()
+ try:
+ result = json.loads(raw)
+ except Exception:
+ print(raw[:2000])
+ raise
+ upload = result.get("upload", {})
+ if not upload:
+ print("RAW RESPONSE:", raw[:2000])
+ print(json.dumps(upload, indent=1))
+ if upload.get("result") != "Success":
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()