Greasy Fork

Greasy Fork is available in English.

ALook 浏览器 脚本直装助手

还原ALook原生安装协议识别并安装user.js后缀的脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         ALook 浏览器 脚本直装助手
// @namespace    http://tampermonkey.net/
// @version      1.4.6
// @description  还原ALook原生安装协议识别并安装user.js后缀的脚本。
// @author       Grok && Gemini
// @match        http*://*/*.user.js
// @match        http*://*/*.userscript.js
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const zhBase64 = {
        encode: function(input) {
            const _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
            let output = "",
                chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;
            input = this.utf8_encode(input);
            while (i < input.length) {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);
                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;
                if (isNaN(chr2)) enc3 = enc4 = 64;
                else if (isNaN(chr3)) enc4 = 64;
                output += _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
            }
            return output;
        },
        utf8_encode: function(_string) {
            return unescape(encodeURIComponent(
                Array.from(_string).map(char => {
                    const codePoint = char.codePointAt(0);
                    if (codePoint > 0x7F) {
                        if (codePoint <= 0xFFFF) {
                            return '\\u' + codePoint.toString(16).padStart(4, '0');
                        } else {
                            return '\\u' + (0xD800 + ((codePoint - 0x10000) >> 10)).toString(16) + '\\u' + (0xDC00 + ((codePoint - 0x10000) & 0x3FF)).toString(16);
                        }
                    }
                    return char;
                }).join('')
            ));
        }
    };

    const extractPureScript = (text) => {
        if (!text) return null;
        const startRegex = /\/\/\s*==UserScript==/i;
        const startMatch = text.match(startRegex);
        if (!startMatch) return null;

        let lines = text.split('\n');
        let lastValidLineIdx = -1;
        for (let i = lines.length - 1; i >= 0; i--) {
            if (lines[i].trim() !== "") {
                lastValidLineIdx = i;
                break;
            }
        }

        if (lastValidLineIdx === -1) return null;

        const lastLineContent = lines[lastValidLineIdx].trim();
        const isEndValid = lastLineContent === ");" || lastLineContent.endsWith("})();") || lastLineContent === "})();";

        if (isEndValid) {
            const cutText = lines.slice(0, lastValidLineIdx + 1).join('\n');
            return cutText.substring(startMatch.index);
        }
        return null;
    };

    const installScript = async () => {
        if (!window.via?.addon) return;

        try {
            let content = extractPureScript(document.body.innerText);
            if (!content) {
                const res = await fetch(location.href, {
                    cache: 'no-cache'
                });
                const remoteText = await res.text();
                content = extractPureScript(remoteText) || remoteText;
            }

            const metaMatch = content.match(/\/\/\s*==UserScript==([\s\S]*?)\/\/\s*==\/UserScript==/i);
            const metaRaw = metaMatch ? metaMatch[1] : '';
            const sensitiveKeywords = ['resource', 'require', 'connect'];
            let foundKeywords = [];
            sensitiveKeywords.forEach(kw => {
                const reg = new RegExp(`\\/\\/\\s*@${kw}\\s+`, 'i');
                if (reg.test(metaRaw)) foundKeywords.push(`@${kw}`);
            });

            if (foundKeywords.length > 0) {
                if (!confirm(`该脚本包含 ALook 可能不支持的指令:\n[ ${foundKeywords.join(', ')} ]\n\n建议检查兼容性。是否继续安装?`)) return;
            }

            const meta = {};
            metaRaw.split('\n').forEach(line => {
                const match = line.match(/\/\/\s*@(\w+)\s+(.*)/);
                if (match) {
                    const key = match[1].toLowerCase();
                    meta[key] = (meta[key] || []).concat(match[2].trim());
                }
            });

            const runAtMatch = metaRaw.match(/\/\/\s*@run-at\s+(.+)/i);
            const runatValue = (!runAtMatch || runAtMatch[1].trim() === 'document-start') ? 1 : 0;

            const config = {
                id: `userscript-${Date.now()}`,
                name: (meta.name || ['未命名脚本'])[0],
                author: (meta.author || ['未知作者'])[0],
                version: (meta.version || ['1.0'])[0],
                runat: runatValue,
                url: (meta.match || meta.include || ['*']).map(rule =>
                    rule.replace(/^https?:\/\//, 'http*://*').replace(/\*/g, '.*')
                ).join('@@'),
                code: btoa(unescape(encodeURIComponent(`(function(){\n\n${content}\n\n})();`)))
            };

            window.via.addon(zhBase64.encode(JSON.stringify(config)));
        } catch (e) {
            console.error(e);
        }
    };

    if (/\.(user|userscript)\.js(\?|$)/i.test(location.href)) {
        if (document.readyState === 'complete') installScript();
        else window.addEventListener('load', installScript, {
            once: true
        });
    }
})();