Greasy Fork

Greasy Fork is available in English.

米游社Web端个人主页显示该用户在所有游戏频道的等级和经验值

米游社Web端个人主页显示该用户在所有游戏频道的等级和经验值。

当前为 2024-02-18 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         米游社Web端个人主页显示该用户在所有游戏频道的等级和经验值
// @namespace    http://tampermonkey.net/
// @version      0.3.4
// @description  米游社Web端个人主页显示该用户在所有游戏频道的等级和经验值。
// @author       aspen138
// @license      MIT
// @match        https://www.miyoushe.com/*/accountCenter/*
// @icon         hhttps://img-static.mihoyo.com/favicon.ico
// @grant       GM_addStyle
// @run-at document-start
// ==/UserScript==



var ajaxHooker = function() {
    'use strict';
    const win = window.unsafeWindow || document.defaultView || window;
    const toString = Object.prototype.toString;
    const getDescriptor = Object.getOwnPropertyDescriptor;
    const hookFns = [];
    const realXhr = win.XMLHttpRequest;
    const realFetch = win.fetch;
    const resProto = win.Response.prototype;
    const xhrResponses = ['response', 'responseText', 'responseXML'];
    const fetchResponses = ['arrayBuffer', 'blob', 'formData', 'json', 'text'];
    const fetchInitProps = ['method', 'headers', 'body', 'mode', 'credentials', 'cache', 'redirect',
        'referrer', 'referrerPolicy', 'integrity', 'keepalive', 'signal', 'priority'];
    const xhrAsyncEvents = ['readystatechange', 'load', 'loadend'];
    let filter;
    function emptyFn() {}
    function errorFn(err) {
        console.error(err);
    }
    function defineProp(obj, prop, getter, setter) {
        Object.defineProperty(obj, prop, {
            configurable: true,
            enumerable: true,
            get: getter,
            set: setter
        });
    }
    function readonly(obj, prop, value = obj[prop]) {
        defineProp(obj, prop, () => value, emptyFn);
    }
    function writable(obj, prop, value = obj[prop]) {
        Object.defineProperty(obj, prop, {
            configurable: true,
            enumerable: true,
            writable: true,
            value: value
        });
    }
    function shouldFilter(type, url, method, async) {
        return filter && !filter.find(obj => {
            switch (true) {
                case obj.type && obj.type !== type:
                case toString.call(obj.url) === '[object String]' && !url.includes(obj.url):
                case toString.call(obj.url) === '[object RegExp]' && !obj.url.test(url):
                case obj.method && obj.method.toUpperCase() !== method.toUpperCase():
                case 'async' in obj && obj.async !== async:
                    return false;
            }
            return true;
        });
    }
    function parseHeaders(obj) {
        const headers = {};
        switch (toString.call(obj)) {
            case '[object String]':
                for (const line of obj.trim().split(/[\r\n]+/)) {
                    const parts = line.split(/\s*:\s*/);
                    if (parts.length !== 2) continue;
                    const lheader = parts[0].toLowerCase();
                    if (lheader in headers) {
                        headers[lheader] += ', ' + parts[1];
                    } else {
                        headers[lheader] = parts[1];
                    }
                }
                return headers;
            case '[object Headers]':
                for (const [key, val] of obj) {
                    headers[key] = val;
                }
                return headers;
            case '[object Object]':
                return {...obj};
            default:
                return headers;
        }
    }
    class AHRequest {
        constructor(request) {
            this.request = request;
            this.requestClone = {...this.request};
            this.response = {};
        }
        waitForHookFns() {
            return Promise.all(hookFns.map(fn => {
                try {
                    return Promise.resolve(fn(this.request)).then(emptyFn, errorFn);
                } catch (err) {
                    console.error(err);
                }
            }));
        }
        waitForResponseFn() {
            try {
                return Promise.resolve(this.request.response(this.response)).then(emptyFn, errorFn);
            } catch (err) {
                console.error(err);
                return Promise.resolve();
            }
        }
        waitForRequestKeys() {
            if (this.reqPromise) return this.reqPromise;
            const requestKeys = ['url', 'method', 'abort', 'headers', 'data'];
            return this.reqPromise = this.waitForHookFns().then(() => Promise.all(
                requestKeys.map(key => Promise.resolve(this.request[key]).then(
                    val => this.request[key] = val,
                    e => this.request[key] = this.requestClone[key]
                ))
            ));
        }
        waitForResponseKeys() {
            if (this.resPromise) return this.resPromise;
            const responseKeys = this.request.type === 'xhr' ? xhrResponses : fetchResponses;
            return this.resPromise = this.waitForResponseFn().then(() => Promise.all(
                responseKeys.map(key => {
                    const descriptor = getDescriptor(this.response, key);
                    if (descriptor && 'value' in descriptor) {
                        return Promise.resolve(descriptor.value).then(
                            val => this.response[key] = val,
                            e => delete this.response[key]
                        );
                    } else {
                        delete this.response[key];
                    }
                })
            ));
        }
    }
    class XhrEvents {
        constructor() {
            this.events = {};
        }
        add(type, event) {
            if (type.startsWith('on')) {
                this.events[type] = typeof event === 'function' ? event : null;
            } else {
                this.events[type] = this.events[type] || new Set();
                this.events[type].add(event);
            }
        }
        remove(type, event) {
            if (type.startsWith('on')) {
                this.events[type] = null;
            } else {
                this.events[type] && this.events[type].delete(event);
            }
        }
        _sIP() {
            this.ajaxHooker_isStopped = true;
        }
        trigger(e) {
            if (e.ajaxHooker_isTriggered || e.ajaxHooker_isStopped) return;
            e.stopImmediatePropagation = this._sIP;
            this.events[e.type] && this.events[e.type].forEach(fn => {
                !e.ajaxHooker_isStopped && fn.call(e.target, e);
            });
            this.events['on' + e.type] && this.events['on' + e.type].call(e.target, e);
            e.ajaxHooker_isTriggered = true;
        }
        clone() {
            const eventsClone = new XhrEvents();
            for (const type in this.events) {
                if (type.startsWith('on')) {
                    eventsClone.events[type] = this.events[type];
                } else {
                    eventsClone.events[type] = new Set([...this.events[type]]);
                }
            }
            return eventsClone;
        }
    }
    const xhrMethods = {
        readyStateChange(e) {
            if (e.target.readyState === 4) {
                e.target.dispatchEvent(new CustomEvent('ajaxHooker_responseReady', {detail: e}));
            } else {
                e.target.__ajaxHooker.eventTrigger(e);
            }
        },
        asyncListener(e) {
            e.target.__ajaxHooker.eventTrigger(e);
        },
        setRequestHeader(header, value) {
            const ah = this.__ajaxHooker;
            ah.originalXhr.setRequestHeader(header, value);
            if (this.readyState !== 1) return;
            if (header in ah.headers) {
                ah.headers[header] += ', ' + value;
            } else {
                ah.headers[header] = value;
            }
        },
        addEventListener(...args) {
            const ah = this.__ajaxHooker;
            if (xhrAsyncEvents.includes(args[0])) {
                ah.proxyEvents.add(args[0], args[1]);
            } else {
                ah.originalXhr.addEventListener(...args);
            }
        },
        removeEventListener(...args) {
            const ah = this.__ajaxHooker;
            if (xhrAsyncEvents.includes(args[0])) {
                ah.proxyEvents.remove(args[0], args[1]);
            } else {
                ah.originalXhr.removeEventListener(...args);
            }
        },
        open(method, url, async = true, ...args) {
            const ah = this.__ajaxHooker;
            ah.url = url.toString();
            ah.method = method.toUpperCase();
            ah.async = !!async;
            ah.openArgs = args;
            ah.headers = {};
            for (const key of xhrResponses) {
                ah.proxyProps[key] = {
                    get: () => {
                        const val = ah.originalXhr[key];
                        ah.originalXhr.dispatchEvent(new CustomEvent('ajaxHooker_readResponse', {
                            detail: {key, val}
                        }));
                        return val;
                    }
                };
            }
            return ah.originalXhr.open(method, url, ...args);
        },
        sendFactory(realSend) {
            return function(data) {
                const ah = this.__ajaxHooker;
                const xhr = ah.originalXhr;
                if (xhr.readyState !== 1) return realSend.call(xhr, data);
                ah.eventTrigger = e => ah.proxyEvents.trigger(e);
                if (shouldFilter('xhr', ah.url, ah.method, ah.async)) {
                    xhr.addEventListener('ajaxHooker_responseReady', e => {
                        ah.eventTrigger(e.detail);
                    }, {once: true});
                    return realSend.call(xhr, data);
                }
                const request = {
                    type: 'xhr',
                    url: ah.url,
                    method: ah.method,
                    abort: false,
                    headers: ah.headers,
                    data: data,
                    response: null,
                    async: ah.async
                };
                if (!ah.async) {
                    const requestClone = {...request};
                    hookFns.forEach(fn => {
                        try {
                            toString.call(fn) === '[object Function]' && fn(request);
                        } catch (err) {
                            console.error(err);
                        }
                    });
                    for (const key in request) {
                        if (toString.call(request[key]) === '[object Promise]') {
                            request[key] = requestClone[key];
                        }
                    }
                    xhr.open(request.method, request.url, ah.async, ...ah.openArgs);
                    for (const header in request.headers) {
                        xhr.setRequestHeader(header, request.headers[header]);
                    }
                    data = request.data;
                    xhr.addEventListener('ajaxHooker_responseReady', e => {
                        ah.eventTrigger(e.detail);
                    }, {once: true});
                    realSend.call(xhr, data);
                    if (toString.call(request.response) === '[object Function]') {
                        const response = {
                            finalUrl: xhr.responseURL,
                            status: xhr.status,
                            responseHeaders: parseHeaders(xhr.getAllResponseHeaders())
                        };
                        for (const key of xhrResponses) {
                            defineProp(response, key, () => {
                                return response[key] = ah.originalXhr[key];
                            }, val => {
                                if (toString.call(val) !== '[object Promise]') {
                                    delete response[key];
                                    response[key] = val;
                                }
                            });
                        }
                        try {
                            request.response(response);
                        } catch (err) {
                            console.error(err);
                        }
                        for (const key of xhrResponses) {
                            ah.proxyProps[key] = {get: () => response[key]};
                        };
                    }
                    return;
                }
                const req = new AHRequest(request);
                req.waitForRequestKeys().then(() => {
                    if (request.abort) return;
                    xhr.open(request.method, request.url, ...ah.openArgs);
                    for (const header in request.headers) {
                        xhr.setRequestHeader(header, request.headers[header]);
                    }
                    data = request.data;
                    xhr.addEventListener('ajaxHooker_responseReady', e => {
                        if (typeof request.response !== 'function') return ah.eventTrigger(e.detail);
                        req.response = {
                            finalUrl: xhr.responseURL,
                            status: xhr.status,
                            responseHeaders: parseHeaders(xhr.getAllResponseHeaders())
                        };
                        for (const key of xhrResponses) {
                            defineProp(req.response, key, () => {
                                return req.response[key] = ah.originalXhr[key];
                            }, val => {
                                delete req.response[key];
                                req.response[key] = val;
                            });
                        }
                        const resPromise = req.waitForResponseKeys().then(() => {
                            for (const key of xhrResponses) {
                                if (!(key in req.response)) continue;
                                ah.proxyProps[key] = {
                                    get: () => {
                                        const val = req.response[key];
                                        xhr.dispatchEvent(new CustomEvent('ajaxHooker_readResponse', {
                                            detail: {key, val}
                                        }));
                                        return val;
                                    }
                                };
                            }
                        });
                        xhr.addEventListener('ajaxHooker_readResponse', e => {
                            const descriptor = getDescriptor(req.response, e.detail.key);
                            if (!descriptor || 'get' in descriptor) {
                                req.response[e.detail.key] = e.detail.val;
                            }
                        });
                        const eventsClone = ah.proxyEvents.clone();
                        ah.eventTrigger = event => resPromise.then(() => eventsClone.trigger(event));
                        ah.eventTrigger(e.detail);
                    }, {once: true});
                    realSend.call(xhr, data);
                });
            };
        }
    };
    function fakeXhr() {
        const xhr = new realXhr();
        let ah = xhr.__ajaxHooker;
        let xhrProxy = xhr;
        if (!ah) {
            const proxyEvents = new XhrEvents();
            ah = xhr.__ajaxHooker = {
                headers: {},
                originalXhr: xhr,
                proxyProps: {},
                proxyEvents: proxyEvents,
                eventTrigger: e => proxyEvents.trigger(e),
                toJSON: emptyFn // Converting circular structure to JSON
            };
            xhrProxy = new Proxy(xhr, {
                get(target, prop) {
                    try {
                        if (target === xhr) {
                            if (prop in ah.proxyProps) {
                                const descriptor = ah.proxyProps[prop];
                                return descriptor.get ? descriptor.get() : descriptor.value;
                            }
                            if (typeof xhr[prop] === 'function') return xhr[prop].bind(xhr);
                        }
                    } catch (err) {
                        console.error(err);
                    }
                    return target[prop];
                },
                set(target, prop, value) {
                    try {
                        if (target === xhr && prop in ah.proxyProps) {
                            const descriptor = ah.proxyProps[prop];
                            descriptor.set ? descriptor.set(value) : (descriptor.value = value);
                        } else {
                            target[prop] = value;
                        }
                    } catch (err) {
                        console.error(err);
                    }
                    return true;
                }
            });
            xhr.addEventListener('readystatechange', xhrMethods.readyStateChange);
            xhr.addEventListener('load', xhrMethods.asyncListener);
            xhr.addEventListener('loadend', xhrMethods.asyncListener);
            for (const evt of xhrAsyncEvents) {
                const onEvt = 'on' + evt;
                ah.proxyProps[onEvt] = {
                    get: () => proxyEvents.events[onEvt] || null,
                    set: val => proxyEvents.add(onEvt, val)
                };
            }
            for (const method of ['setRequestHeader', 'addEventListener', 'removeEventListener', 'open']) {
                ah.proxyProps[method] = { value: xhrMethods[method] };
            }
        }
        ah.proxyProps.send = { value: xhrMethods.sendFactory(xhr.send) };
        return xhrProxy;
    }
    function hookFetchResponse(response, req) {
        for (const key of fetchResponses) {
            response[key] = () => new Promise((resolve, reject) => {
                if (key in req.response) return resolve(req.response[key]);
                resProto[key].call(response).then(res => {
                    req.response[key] = res;
                    req.waitForResponseKeys().then(() => {
                        resolve(key in req.response ? req.response[key] : res);
                    });
                }, reject);
            });
        }
    }
    function fakeFetch(url, options = {}) {
        if (!url) return realFetch.call(win, url, options);
        let init = {...options};
        if (toString.call(url) === '[object Request]') {
            init = {};
            for (const prop of fetchInitProps) init[prop] = url[prop];
            Object.assign(init, options);
            url = url.url;
        }
        url = url.toString();
        init.method = init.method || 'GET';
        init.headers = init.headers || {};
        if (shouldFilter('fetch', url, init.method, true)) return realFetch.call(win, url, init);
        const request = {
            type: 'fetch',
            url: url,
            method: init.method.toUpperCase(),
            abort: false,
            headers: parseHeaders(init.headers),
            data: init.body,
            response: null,
            async: true
        };
        const req = new AHRequest(request);
        return new Promise((resolve, reject) => {
            req.waitForRequestKeys().then(() => {
                if (request.abort) return reject(new DOMException('aborted', 'AbortError'));
                init.method = request.method;
                init.headers = request.headers;
                init.body = request.data;
                realFetch.call(win, request.url, init).then(response => {
                    if (typeof request.response === 'function') {
                        req.response = {
                            finalUrl: response.url,
                            status: response.status,
                            responseHeaders: parseHeaders(response.headers)
                        };
                        hookFetchResponse(response, req);
                        response.clone = () => {
                            const resClone = resProto.clone.call(response);
                            hookFetchResponse(resClone, req);
                            return resClone;
                        };
                    }
                    resolve(response);
                }, reject);
            }).catch(err => {
                console.error(err);
                resolve(realFetch.call(win, url, init));
            });
        });
    }
    win.XMLHttpRequest = fakeXhr;
    Object.keys(realXhr).forEach(key => fakeXhr[key] = realXhr[key]);
    fakeXhr.prototype = realXhr.prototype;
    win.fetch = fakeFetch;
    return {
        hook: fn => hookFns.push(fn),
        filter: arr => {
            filter = Array.isArray(arr) && arr;
        },
        protect: () => {
            readonly(win, 'XMLHttpRequest', fakeXhr);
            readonly(win, 'fetch', fakeFetch);
        },
        unhook: () => {
            writable(win, 'XMLHttpRequest', realXhr);
            writable(win, 'fetch', realFetch);
        }
    };
}();



var formattedString = "place_holder";

function insertOrUpdateFormattedString() {
    // Use an ID or a class to identify the element
    var existingElement = document.getElementById('formattedStringElement');

    if (existingElement) {
        // If the element already exists, update its content
        existingElement.innerHTML = "\r\n<br>" + formattedString;
    } else {
        // Otherwise, create and insert the element as before
        var formattedStringElement = document.createElement('div');
        formattedStringElement.id = 'formattedStringElement'; // Assign an ID to the new element
        formattedStringElement.innerHTML = "\r\n<br>" + formattedString;
        var introDivs = document.querySelectorAll('.mhy-account-center-user__intro');

        if (introDivs && introDivs.length > 1) {
            var targetDiv = introDivs[1];
            targetDiv.parentNode.insertBefore(formattedStringElement, targetDiv.nextSibling);
        }
    }
}

(function() {
    'use strict';

    // Assuming ajaxHooker is already defined elsewhere in your script
    ajaxHooker.hook(request => {
        if (request.url.includes("https://bbs-api.miyoushe.com/user/wapi/getUserFullInfo")) {
            request.response = res => {
                var json = JSON.parse(res.responseText);
                var gameLevelExpList = json.data.user_info.level_exps;
                var gameNames = {
                    1: "崩坏3",
                    2: "原神",
                    3: "崩坏学园2",
                    4: "未定事件簿",
                    5: "大别野",
                    6: "星穹铁道",
                    8: "绝区零"
                };

                formattedString = gameLevelExpList.map(function(item) {
                    var gameName = gameNames[item.game_id] || "未知游戏";
                    return `${gameName}频道等级为${item.level},经验为${item.exp}<br>`;
                }).join("") + "";

                // Call the function to either insert or update the string in the page
                insertOrUpdateFormattedString();
            }
        }
    });
})();