1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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()
|