GM Requests

Imitating Python's Requests library based on GM_xmlHttpRequest.

目前為 2023-07-03 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/470000/1214515/GM%20Requests.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        GM Requests
// @description Imitating Python's Requests library based on GM_xmlHttpRequest.
// @version     0.0.2
// @author      大碗宽面wtw
// @homepage    https://github.com/bigbowl-wtw/gm-requests/
// @supportURL  https://github.com/bigbowl-wtw/gm-requests/issues
// @match       *://*/*
// @grant       GM_xmlhttpRequest
// @license     MIT
// @namespace   com.github.bigbowl-wtw
// ==/UserScript==

(function webpackUniversalModuleDefinition(root, factory) {
    if (typeof exports === "object" && typeof module === "object") module.exports = factory(); else if (typeof define === "function" && define.amd) define([], factory); else if (typeof exports === "object") exports["requests"] = factory(); else root["requests"] = factory();
})(self, (() => (() => {
    "use strict";
    var __webpack_require__ = {};
    (() => {
        __webpack_require__.d = (exports, definition) => {
            for (var key in definition) {
                if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
                    Object.defineProperty(exports, key, {
                        enumerable: true,
                        get: definition[key]
                    });
                }
            }
        };
    })();
    (() => {
        __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
    })();
    (() => {
        __webpack_require__.r = exports => {
            if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, {
                    value: "Module"
                });
            }
            Object.defineProperty(exports, "__esModule", {
                value: true
            });
        };
    })();
    var __webpack_exports__ = {};
    __webpack_require__.r(__webpack_exports__);
    __webpack_require__.d(__webpack_exports__, {
        Session: () => Session,
        get: () => get,
        post: () => post,
        session: () => session
    });
    var ErrorStatus;
    (function(ErrorStatus) {
        ErrorStatus[ErrorStatus["BadRequest"] = 400] = "BadRequest";
        ErrorStatus[ErrorStatus["Unauthorized"] = 401] = "Unauthorized";
        ErrorStatus[ErrorStatus["Forbidden"] = 403] = "Forbidden";
        ErrorStatus[ErrorStatus["NotFound"] = 404] = "NotFound";
        ErrorStatus[ErrorStatus["MethodNotAllowed"] = 405] = "MethodNotAllowed";
        ErrorStatus[ErrorStatus["RequestTimeout"] = 408] = "RequestTimeout";
        ErrorStatus[ErrorStatus["TooManyRequests"] = 429] = "TooManyRequests";
        ErrorStatus[ErrorStatus["InternalServerError"] = 500] = "InternalServerError";
        ErrorStatus[ErrorStatus["BadGateway"] = 502] = "BadGateway";
        ErrorStatus[ErrorStatus["ServiceUnavailable"] = 503] = "ServiceUnavailable";
    })(ErrorStatus || (ErrorStatus = {}));
    function utils_assign(target, ...rest) {
        const __assign = Object.assign || function(t, ...others) {
            for (let s, i = 0, n = others.length; i < n; i++) {
                s = others[i];
                for (const p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
            }
            return t;
        };
        return __assign.call(this, target, ...rest);
    }
    function assignDeepCopy(target, ...rest) {
        rest = rest.filter((r => r !== undefined));
        utils_assign(target, ...rest);
        return utils_assign.call(this, target, ...JSON.parse(JSON.stringify(rest)));
    }
    function bodyToString(data) {
        if (Object.values(data).some((v => typeof v.toString !== "function"))) throw new TypeError("value must has `.toString`");
        return Object.entries(data).map((([key, value]) => `${key}=${encodeURIComponent(value.toString())}`)).join("&");
    }
    class SimpleCookieJar {
        get empty() {
            return !Object.keys(this.cookies).length;
        }
        constructor(cookies) {
            var _a;
            this.never = new Set;
            this.setCookies((_a = cookies) !== null && _a !== void 0 ? _a : {});
        }
        update(cookies) {
            utils_assign(this.cookies, this.normalizeCookie(cookies));
        }
        updateFromString(cookieStringArray) {
            if (typeof cookieStringArray === "string") cookieStringArray = [ cookieStringArray ];
            this.update(cookieStringArray);
        }
        differenceUpdate(cookies) {
            let cookieEntries;
            if (Array.isArray(cookies)) cookieEntries = cookies.map(this.stringToEntry); else cookieEntries = Object.entries(isCookieJar(cookies) ? cookies.cookies : cookies);
            const set = new Set(Object.keys(this.cookies));
            cookieEntries = cookieEntries.filter((([name]) => !set.has(name)));
            this.update(Object.fromEntries(cookieEntries));
        }
        deleteFromString(cookieStringArray) {
            if (typeof cookieStringArray === "string") cookieStringArray = [ cookieStringArray ];
            const names = cookieStringArray.map(this.stringToEntry).map((([name]) => name));
            names.forEach((name => {
                this.never.add(name);
                delete this.cookies[name];
            }));
        }
        setCookies(cookies) {
            this.cookies = new Proxy(this.normalizeCookie(cookies), {
                set: (target, name, value) => {
                    if (this.never.has(name)) return true;
                    target[name] = value;
                    return true;
                }
            });
        }
        toString() {
            return Object.entries(this.cookies).map((([k, v]) => `${k}=${v}`)).join(";");
        }
        parseCookieString(cookieStrings) {
            return Object.fromEntries(cookieStrings.map(this.stringToEntry));
        }
        normalizeCookie(cookies) {
            if (Array.isArray(cookies)) return this.parseCookieString(cookies);
            if (isCookieJar(cookies)) return cookies.cookies;
            return cookies;
        }
        stringToEntry(cookieString) {
            return cookieString.slice(0, cookieString.indexOf(";")).split("=");
        }
    }
    function isCookieJar(obj) {
        return typeof obj.update === "function";
    }
    var __rest = undefined && undefined.__rest || function(s, e) {
        var t = {};
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
        if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
        }
        return t;
    };
    class Header {
        constructor(headers) {
            this.cookies = new SimpleCookieJar;
            if (!headers) {
                this.headers = {};
                return;
            }
            const {cookie} = headers, others = __rest(headers, [ "cookie" ]);
            this.headers = others;
            if (cookie) {
                this.cookies.update(cookie);
            }
        }
        setFromString(headerString) {
            const entrise = headerString.split("\r\n").map((kv => kv.split(":"))).map((([k, v]) => [ k.toLowerCase(), v.trim() ]));
            for (const [name, value] of entrise) {
                const header = this.headers[name];
                if (header) {
                    if (typeof header === "string") this.headers[name] = [ header ];
                    this.headers[name].push(value);
                } else {
                    this.headers[name] = value;
                }
            }
            return this;
        }
        update(headers) {
            if (isIHeader(headers)) {
                this.cookies.update(headers.cookies);
                this.update(headers.headers);
            } else {
                const _a = headers !== null && headers !== void 0 ? headers : {}, {cookie} = _a, others = __rest(_a, [ "cookie" ]);
                utils_assign(this.headers, others);
                if (cookie) {
                    this.cookies.update(cookie);
                }
            }
        }
        getHeaders() {
            if (!Object.keys(this.headers).length) {
                if (this.cookies.empty) return {};
                return {
                    cookie: this.cookies.toString()
                };
            }
            const headers = {};
            for (const [name, value] of Object.entries(this.headers)) headers[name] = value.toString();
            return Object.assign(Object.assign({}, headers), {
                cookie: this.cookies.toString()
            });
        }
        append(header, value) {
            if (header === "cookie") this.cookies.update([ header ]); else if (!this[header]) this[header] = value; else if (!Array.isArray(header)) this[header] = [ value ].concat(this[header]); else this[header].concat(value);
        }
        get(name) {
            return this.headers[name];
        }
    }
    function isIHeader(obj) {
        return typeof obj.update === "function";
    }
    var __awaiter = undefined && undefined.__awaiter || function(thisArg, _arguments, P, generator) {
        function adopt(value) {
            return value instanceof P ? value : new P((function(resolve) {
                resolve(value);
            }));
        }
        return new (P || (P = Promise))((function(resolve, reject) {
            function fulfilled(value) {
                try {
                    step(generator.next(value));
                } catch (e) {
                    reject(e);
                }
            }
            function rejected(value) {
                try {
                    step(generator["throw"](value));
                } catch (e) {
                    reject(e);
                }
            }
            function step(result) {
                result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
            }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        }));
    };
    var session_rest = undefined && undefined.__rest || function(s, e) {
        var t = {};
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
        if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
        }
        return t;
    };
    class Details {
        constructor(url, method, options) {
            this.pendings = [];
            this.url = url;
            this.method = method;
            this.finalHeader = new Header;
            if (options) {
                const {query, json, data, cookie, auth, headers} = options, others = session_rest(options, [ "query", "json", "data", "cookie", "auth", "headers" ]);
                this.query = query;
                this.json = json;
                this.data = data;
                this.cookie = cookie;
                this.auth = auth;
                this.headers = headers;
                this.others = others;
            }
        }
        build(session) {
            return __awaiter(this, void 0, void 0, (function*() {
                this.buildURL().buildHeaderAndCookie(session).buildAuth(session);
                this.pendings.concat(session.buildHooks.map((hook => hook.call(this, session))));
                return this.toDetails();
            }));
        }
        buildURL() {
            this.parseQuery(this.query);
            return this;
        }
        buildHeaderAndCookie(session) {
            const finalCookie = this.finalHeader.cookies;
            if (!session.cookies.empty) finalCookie.update(session.cookies);
            if (this.cookie) finalCookie.update(this.cookie);
            this.finalHeader.update(session.headers);
            if (this.headers) this.finalHeader.update(this.headers);
            return this;
        }
        buildAuth(session) {
            var _a;
            this.pendings.push((_a = session.auth) === null || _a === void 0 ? void 0 : _a.build(this.finalHeader));
            return this;
        }
        buildBody() {
            if (this.json) {
                const contentType = "application/json";
                this.finalHeader.update({
                    "Content-Type": contentType
                });
                const data = JSON.stringify(this.json);
                this.finalData = data;
                return this;
            }
            if (this.data) {
                if (typeof this.data === "string") {
                    this.finalData = this.data;
                    return this;
                }
                if (this.data instanceof FormData) return this;
                const contentType = "application/x-www-form-urlencoded";
                this.finalHeader.update({
                    "Content-Type": contentType
                });
                this.finalData = bodyToString(this.data);
                return this;
            }
            return this;
        }
        toDetails() {
            return __awaiter(this, void 0, void 0, (function*() {
                yield Promise.all(this.pendings);
                const url = this.url.toString();
                this.buildBody();
                const details = Object.assign({
                    url,
                    method: this.method,
                    headers: this.finalHeader.getHeaders()
                }, this.others);
                if (this.finalData) details.data = this.finalData;
                return details;
            }));
        }
        parseQuery(query) {
            let url;
            if (typeof this.url === "string") url = new URL(this.url); else url = this.url;
            if (query) Object.entries(query).filter((([_, v]) => typeof v === "string")).forEach((([k, v]) => url.searchParams.append(k, v.toString())));
            this.url = url;
        }
    }
    class Session {
        constructor() {
            this.buildHooks = [];
            const headers = new Header;
            Object.defineProperty(this, "headers", {
                get: () => headers,
                set(value) {
                    headers.update(value);
                }
            });
            Object.defineProperty(this, "cookies", {
                get: () => headers.cookies,
                set(value) {
                    headers.cookies.setCookies(value);
                }
            });
        }
        get(url, options) {
            return __awaiter(this, void 0, void 0, (function*() {
                return this.request("GET", url, options);
            }));
        }
        post(url, options) {
            return __awaiter(this, void 0, void 0, (function*() {
                return this.request("POST", url, options);
            }));
        }
        request(method, url, options) {
            return __awaiter(this, void 0, void 0, (function*() {
                const details = new Details(url, method, options);
                return new Promise(((resolve, reject) => {
                    if (!options.onload) {
                        details.others.onload = resp => {
                            if (!this.cookies.empty) {
                                const respHeaders = (new Header).setFromString(resp.responseHeaders);
                                this.cookies.deleteFromString(respHeaders["set-cookie"]);
                            }
                            if (resp.status === 200 && !(resp.status in ErrorStatus)) resolve((options === null || options === void 0 ? void 0 : options.responseType) ? resp.response : resp); else reject(resp);
                        };
                    }
                    if (!options.onerror) details.others.onerror = resp => reject(resp);
                    details.build(this).then((dtl => GM_xmlhttpRequest(dtl)));
                }));
            }));
        }
        registerBuildHook(hook) {
            this.buildHooks.push(hook);
        }
    }
    function get(url, query, options) {
        return (new Session).get(url, Object.assign({
            query
        }, options));
    }
    function post(url, options) {
        return (new Session).post(url, Object.assign({}, options));
    }
    function session() {
        return new Session;
    }
    return __webpack_exports__;
})()));