wiki.py 4.44 KB
Newer Older
Robin Sonnabend's avatar
Robin Sonnabend committed
1 2 3 4 5
import requests

import config

HTTP_STATUS_OK = 200
6
HTTP_STATUS_AUTHENTICATE = 401
Robin Sonnabend's avatar
Robin Sonnabend committed
7

Robin Sonnabend's avatar
Robin Sonnabend committed
8

Robin Sonnabend's avatar
Robin Sonnabend committed
9 10 11
class WikiException(Exception):
    pass

Robin Sonnabend's avatar
Robin Sonnabend committed
12

Robin Sonnabend's avatar
Robin Sonnabend committed
13 14 15 16 17 18 19 20 21 22
def _filter_params(params):
    result = {}
    for key, value in sorted(params.items(), key=lambda t: t[0] == "token"):
        if isinstance(value, bool):
            if value:
                result[key] = ""
        else:
            result[key] = value
    return result

Robin Sonnabend's avatar
Robin Sonnabend committed
23

Robin Sonnabend's avatar
Robin Sonnabend committed
24
class WikiClient:
Robin Sonnabend's avatar
Robin Sonnabend committed
25 26 27 28 29 30 31 32 33 34 35 36
    def __init__(self, active=None, endpoint=None, anonymous=None, user=None,
                 password=None, domain=None):
        def _or_default(value, default):
            if value is None:
                return default
            return value
        self.active = _or_default(active, config.WIKI_ACTIVE)
        self.endpoint = _or_default(endpoint, config.WIKI_API_URL)
        self.anonymous = _or_default(anonymous, config.WIKI_ANONYMOUS)
        self.user = _or_default(user, config.WIKI_USER)
        self.password = _or_default(password, config.WIKI_PASSWORD)
        self.domain = _or_default(domain, config.WIKI_DOMAIN)
Robin Sonnabend's avatar
Robin Sonnabend committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
        self.token = None
        self.cookies = requests.cookies.RequestsCookieJar()

    def __enter__(self):
        if not self.anonymous:
            self.login()
        return self

    def __exit__(self, type, value, traceback):
        if not self.anonymous:
            self.logout()

    def is_logged_in(self):
        return self.token is not None

    def login(self):
        if not self.active:
            return
Robin Sonnabend's avatar
Robin Sonnabend committed
55
        # todo: Change this to the new MediaWiki tokens api
Robin Sonnabend's avatar
Robin Sonnabend committed
56 57 58 59
        token_answer = self.do_action("login", method="post", lgname=self.user)
        if "login" not in token_answer or "token" not in token_answer["login"]:
            raise WikiException("No token in login answer.")
        lgtoken = token_answer["login"]["token"]
Robin Sonnabend's avatar
Robin Sonnabend committed
60 61 62
        login_answer = self.do_action(
            "login", method="post", lgname=self.user, lgpassword=self.password,
            lgdomain=self.domain, lgtoken=lgtoken)
Robin Sonnabend's avatar
Robin Sonnabend committed
63
        if ("login" not in login_answer
Robin Sonnabend's avatar
Robin Sonnabend committed
64 65
                or "result" not in login_answer["login"]
                or login_answer["login"]["result"] != "Success"):
Robin Sonnabend's avatar
Robin Sonnabend committed
66 67 68 69 70 71 72
            raise WikiException("Login not successful.")

    def logout(self):
        if not self.active:
            return
        self.do_action("logout")

Robin Sonnabend's avatar
Robin Sonnabend committed
73 74
    def edit_page(self, title, content, summary, recreate=True,
                  createonly=False):
Robin Sonnabend's avatar
Robin Sonnabend committed
75 76 77
        if not self.active:
            return
        # todo: port to new api once the wiki is updated
Robin Sonnabend's avatar
Robin Sonnabend committed
78 79
        prop_answer = self.do_action(
            "query", method="get", prop="info", intoken="edit", titles=title)
Robin Sonnabend's avatar
Robin Sonnabend committed
80
        if ("query" not in prop_answer
Robin Sonnabend's avatar
Robin Sonnabend committed
81
                or "pages" not in prop_answer["query"]):
Robin Sonnabend's avatar
Robin Sonnabend committed
82 83 84 85 86 87 88 89 90
            raise WikiException("Can't get token for page {}".format(title))
        pages = prop_answer["query"]["pages"]
        edit_token = None
        for page in pages.values():
            if page["title"] == title:
                edit_token = page["edittoken"]
                break
        if edit_token is None:
            raise WikiException("Can't get token for page {}".format(title))
Robin Sonnabend's avatar
Robin Sonnabend committed
91 92
        self.do_action(
            action="edit", method="post", data={"text": content},
Robin Sonnabend's avatar
Robin Sonnabend committed
93 94 95 96 97 98 99 100 101 102
            token=edit_token, title=title,
            summary=summary, recreate=recreate,
            createonly=createonly, bot=True)

    def do_action(self, action, method="get", data=None, **kwargs):
        if not self.active:
            return
        kwargs["action"] = action
        kwargs["format"] = "json"
        params = _filter_params(kwargs)
Robin Sonnabend's avatar
Robin Sonnabend committed
103

104 105
        def _do_request():
            if method == "get":
Robin Sonnabend's avatar
Robin Sonnabend committed
106 107 108
                return requests.get(
                    self.endpoint, cookies=self.cookies, params=params,
                    auth=requests.auth.HTTPBasicAuth(self.user, self.password))
109
            elif method == "post":
Robin Sonnabend's avatar
Robin Sonnabend committed
110 111 112 113
                return requests.post(
                    self.endpoint, cookies=self.cookies, data=data,
                    params=params, auth=requests.auth.HTTPBasicAuth(
                        self.user, self.password))
114
        req = _do_request()
Robin Sonnabend's avatar
Robin Sonnabend committed
115
        if req.status_code != HTTP_STATUS_OK:
Robin Sonnabend's avatar
Robin Sonnabend committed
116 117 118
            raise WikiException(
                "HTTP status code {} on action {}.".format(
                    req.status_code, action))
119
        self.cookies.update(req.cookies)
Robin Sonnabend's avatar
Robin Sonnabend committed
120 121
        return req.json()

Robin Sonnabend's avatar
Robin Sonnabend committed
122

Robin Sonnabend's avatar
Robin Sonnabend committed
123 124
def main():
    with WikiClient() as client:
Robin Sonnabend's avatar
Robin Sonnabend committed
125 126 127
        client.edit_page(
            title="Test", content="This is a very long text.",
            summary="API client test")