Greasy Fork is available in English.

xfgryujk's bliveproxy

从xfgryujk哪里抄来的,忽略了一个报错的地方

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/434638/984839/xfgryujk%27s%20bliveproxy.js

// ==UserScript==
// @license             MIT
// @original-script     https://github.com/xfgryujk/bliveproxy
// @original-author     xfgryujk
// @original-license    MIT
// ==/UserScript==

// MIT License

// Copyright (c) 2020 xfgryujk

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

(function () {
    const HEADER_SIZE = 16;

    const WS_BODY_PROTOCOL_VERSION_NORMAL = 0;
    const WS_BODY_PROTOCOL_VERSION_HEARTBEAT = 1;
    const WS_BODY_PROTOCOL_VERSION_DEFLATE = 2;
    const WS_BODY_PROTOCOL_VERSION_BROTLI = 3;

    const OP_HEARTBEAT_REPLY = 3;
    const OP_SEND_MSG_REPLY = 5;

    let textEncoder = new TextEncoder();
    let textDecoder = new TextDecoder();

    function main() {
        if (unsafeWindow.bliveproxy) {
            // 防止多次加载
            return;
        }
        initApi();
        hook();
    }

    function initApi() {
        unsafeWindow.bliveproxy = api;
    }

    let api = {
        addCommandHandler(cmd, handler) {
            let handlers = this._commandHandlers[cmd];
            if (!handlers) {
                handlers = this._commandHandlers[cmd] = [];
            }
            handlers.push(handler);
        },
        removeCommandHandler(cmd, handler) {
            let handlers = this._commandHandlers[cmd];
            if (!handlers) {
                return;
            }
            this._commandHandlers[cmd] = handlers.filter(item => item !== handler);
        },

        // 私有API
        _commandHandlers: {},
        _getCommandHandlers(cmd) {
            return this._commandHandlers[cmd] || null;
        }
    }

    function hook() {
        unsafeWindow.WebSocket = new Proxy(unsafeWindow.WebSocket, {
            construct(target, args) {
                let obj = new target(...args);
                return new Proxy(obj, proxyHandler);
            }
        });
    }

    let proxyHandler = {
        get(target, property) {
            let value = target[property];
            if ((typeof value) === 'function') {
                value = value.bind(target);
            }
            return value;
        },
        set(target, property, value) {
            if (property === 'onmessage') {
                let realOnMessage = value;
                value = function (event) {
                    myOnMessage(event, realOnMessage);
                }
            }
            target[property] = value;
            return value;
        }
    }

    function myOnMessage(event, realOnMessage) {
        if (!(event.data instanceof ArrayBuffer)) {
            realOnMessage(event);
            return;
        }

        let data = new Uint8Array(event.data);
        function callRealOnMessageByPacket(packet) {
            realOnMessage({ ...event, data: packet });
        }
        handleMessage(data, callRealOnMessageByPacket);
    }

    function makePacketFromCommand(command) {
        let body = textEncoder.encode(JSON.stringify(command));
        return makePacketFromUint8Array(body, OP_SEND_MSG_REPLY);
    }

    function makePacketFromUint8Array(body, operation) {
        let packLen = HEADER_SIZE + body.byteLength;
        let packet = new ArrayBuffer(packLen);

        // 不需要压缩
        let ver = operation === OP_HEARTBEAT_REPLY ? WS_BODY_PROTOCOL_VERSION_HEARTBEAT : WS_BODY_PROTOCOL_VERSION_NORMAL;
        let packetView = new DataView(packet);
        packetView.setUint32(0, packLen);        // pack_len
        packetView.setUint16(4, HEADER_SIZE);    // raw_header_size
        packetView.setUint16(6, ver);            // ver
        packetView.setUint32(8, operation);      // operation
        packetView.setUint32(12, 1);             // seq_id

        let packetBody = new Uint8Array(packet, HEADER_SIZE, body.byteLength);
        for (let i = 0; i < body.byteLength; i++) {
            packetBody[i] = body[i];
        }
        return packet;
    }

    function handleMessage(data, callRealOnMessageByPacket) {
        let offset = 0;
        while (offset < data.byteLength) {
            let dataView = new DataView(data.buffer, offset);
            let packLen = dataView.getUint32(0);
            if (packLen > 1534026300) { return; }
            // let rawHeaderSize = dataView.getUint16(4);
            let ver = dataView.getUint16(6);
            let operation = dataView.getUint32(8);
            // let seqId = dataView.getUint32(12);

            let body = new Uint8Array(data.buffer, offset + HEADER_SIZE, packLen - HEADER_SIZE);
            if (operation === OP_SEND_MSG_REPLY) {
                switch (ver) {
                    case WS_BODY_PROTOCOL_VERSION_NORMAL:
                        body = textDecoder.decode(body);
                        body = JSON.parse(body);
                        handleCommand(body, callRealOnMessageByPacket);
                        break;
                    case WS_BODY_PROTOCOL_VERSION_DEFLATE:
                        body = pako.inflate(body);
                        handleMessage(body, callRealOnMessageByPacket);
                        break;
                    case WS_BODY_PROTOCOL_VERSION_BROTLI:
                        body = BrotliDecode(body);
                        handleMessage(body, callRealOnMessageByPacket);
                        break;
                    default: {
                        let packet = makePacketFromUint8Array(body, operation);
                        callRealOnMessageByPacket(packet);
                        break;
                    }
                }
            } else {
                let packet = makePacketFromUint8Array(body, operation);
                callRealOnMessageByPacket(packet);
            }

            offset += packLen;
        }
    }

    function handleCommand(command, callRealOnMessageByPacket) {
        if (command instanceof Array) {
            for (let oneCommand of command) {
                this.handleCommand(oneCommand);
            }
            return;
        }

        let cmd = command.cmd || '';
        let pos = cmd.indexOf(':');
        if (pos != -1) {
            cmd = cmd.substr(0, pos);
        }
        let handlers = api._getCommandHandlers(cmd);
        if (handlers) {
            for (let handler of handlers) {
                handler(command);
            }
        }

        let packet = makePacketFromCommand(command);
        callRealOnMessageByPacket(packet);
    }

    main();
})();