膜法小工具

方便生活,快乐分享

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         膜法小工具
// @version      0.6.2
// @author       dolacmeo
// @description  方便生活,快乐分享
// @license      MIT; https://opensource.org/licenses/MIT
// @namespace    https://greasyfork.org/users/57661
// @namespace    https://greasyfork.org/zh-CN/scripts/37822/
// @supportURL   https://greasyfork.org/zh-CN/scripts/37822/feedback
// @require      https://cdn.jsdelivr.net/npm/[email protected]/base64.min.js
// @resource     country_code https://w.url.cn/s/AQenoiI
// @include      *://free-ss.*
// @grant        GM_log
// @grant        GM_info
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @grant        GM_getResourceText
// @grant        GM_xmlhttpRequest
// @grant        GM_openInTab
// @grant        unsafeWindow
// @run-at       document-idle
// ==/UserScript==
// @0.6.2 2020-07-10   代码优化、更换订阅转换接口
// @0.6.1 2020-07-08   错误修复、使用paste.rs充当临时订阅
// @0.6.0 2020-06-15   错误修复、样式优化
//                     抱歉很久没更新,报错是因为淘宝ip接口已不支持公用、移除了客户端检查
// @0.5.9 2018-05-15   样式优化
// @0.5.8 2018-05-14   样式优化、错误修复、新增功能
//                     鼠标悬停显示二维码、增加移除客户端无效协议功能
// @0.5.7 2018-05-11   样式优化
//                     预更新线路检测功能
// @0.5.6 2018-05-09   样式优化
//                     移除低速后移除无效区域
// @0.5.5 2018-05-04   样式优化
// @0.5.4 2018-05-04   样式优化、代码优化
//                     优化页面样式,中文化区域选择
// @0.5.3 2018-05-03   错误修复、可用性维护
// @0.5.2 2018-05-02   样式优化、新增功能
// @0.5.1 2018-04-27   错误修复
// @0.5.0 2018-04-27   样式优化、代码优化
//                     变更了插件名称及其他信息
// @0.4.6 2018-04-23   应站长要求移除订阅功能
// @0.4.5 2018-04-23   可用性维护
// @0.4.4 2018-04-09   错误修复
// @0.4.2 2018-04-08   新增功能
// @0.4.1 2018-04-07   错误修复、新增功能
//                     新增移除低速按钮,一键清爽(6分以下)
// @0.4.0 2018-04-05   代码优化
// @0.3.8 2018-03-22   可用性维护
// @0.3.7 2018-03-03   可用性维护
// @0.3.6 2018-02-27   可用性维护
//                     增加镜像站
// @0.3.5 2018-02-24   可用性维护
// @0.3.4 2018-02-13   代码优化
// @0.3.3 2018-02-06   可用性维护
// @0.3.2 2018-02-02   样式优化
//                     点击二维码按钮生成两种链接与二维码
// @0.3.1 2018-02-02   样式优化、代码优化
//                     可进行多选,再生成链接,不选择生成所有
// @0.3.0 2018-02-02   可用性维护、代码优化
// @0.2.1 2018-01-31   错误修复
// @0.2.0 2018-01-31   样式优化、新增功能
// @0.1.0 2018-01-26   初始版本

// $(".banner").remove(); // 去广告
var today_date = new Date();
var date_str = today_date.toISOString().slice(0,10)+'_';
var enb64 = Base64.encodeURI, deb64 = Base64.decode;
var ss_id, ss_links_str = "", ssr_links_str = "",rss_url,
    link_count=0, areas=[], xyz="http://"+deb64("c3NyLjEyMzQ1NjYueHl6"),
    ok_method=['aes-128-cfb','aes-128-ctr','aes-192-cfb','aes-192-ctr','aes-256-cfb','aes-256-ctr',
               'bf-cfb','camellia-128-cfb','camellia-192-cfb','camellia-256-cfb','chacha20','chacha20-ietf','rc4-md5','salsa20'],
    order={point:0,address:1,port:2,password:3,method:4,clock:5,globe:6,qrcode:7};
var country_code = JSON.parse(""+GM_getResourceText('country_code'));
$("table").each(function (){if ($("#"+this.id+"_wrapper").css("height") === undefined) {ss_id = "#"+this.id;}});
layer.load(0, {shade:false,time:3000});$(ss_id).before("<ul id='tools'></ul>");
$('#qrcode').after('<div style="display:none" id="qrcode0"></div>');
GM_addStyle("body{margin:0;}"+
            "h3 small a{font-weight:bold;color:#4CAF50;font-size:10px;text-decoration:none;}"+
            "h3 small button{padding:2px 5px;border:none;font-size:1em;cursor:pointer;}"+
            "li.a {padding:0 40px;}li.q {padding:0 20px;}"+
            "li.aff {float:none;display:inline-block;}"+
            "#tools {margin:0;padding-left:10px;}"+
            "#tools p{margin:0;height:23px;}"+"#sel{color:#000080}"+
            "#tools button{cursor:pointer;margin-left:3px;height:23px;border: 0;}"+
            "#tools p,#tools span,#tools small{cursor:default;}"+
            "#tools .txt{display:inline-block;float:left;font-weight:bold;color:#f66;}"+
            "#tools .btn{display:inline-block;float:right;}"+
            "#tools .btn small{color:#e91e63;font-weight: bold;}"+
            ".qr-link {width: 298px;display: inline-block;background-color: darkgrey;border: darkgreen solid 1px;}"+
            ".qr-link a, .qr-link input[type='checkbox']{font-size: 30px;font-weight: bold;}.qr-link input[type='checkbox'] {width: 20px; height: 20px;float: right;}"+
            ".qr-link canvas {width: 90%;margin: 10px;}"+
            ".showup {opacity: 1;} .showoff {opacity: 0;}"+
            "#ss_area {display:inline-block;float:right;margin-left:3px;padding:1px 6px;height:23px;cursor:pointer;color:blue;}");

// 工具对象
var tools = {
    // 查询当前页面次序
    order: function () {
        var o = [],d = {}, v;
        $(ss_id).find("th").each(function () {
            v = $(this).html();
            if (v.split("/").length-1 >= 2) {
                o.push("point");
            } else if (v.search("clock") != -1) {
                o.push("clock");
            } else if (v.search("globe") != -1) {
                o.push("globe");
            } else if (v.search("qrcode") != -1) {
                o.push("qrcode");
            } else {o.push(v.toLowerCase());}
        });
        for (var x=0;x<o.length;x++) {d[o[x]] = x;}
        return d;
    },
    // 整理链接区域
    area: function() {
        var ssdatas = ss_table.data(), column = 6, l=[];
        if (order != undefined) {column = order.globe;}
        $.each(ssdatas, function(i, data){if ($.inArray(data[column], l) == -1) {if (data[column][0] != '*'){l.push(data[column]);}}});
        areas = l;return l;
    },
    // ss://method:password@server:port
    ss: function (data) {
        var method=data[order.method],
            password=data[order.password],
            server=data[order.address],
            port=data[order.port],
            remark=data[order.globe]+'['+data[order.point]+']'+'('+date_str+data[order.clock]+')';
        return 'ss://'+enb64(method+':'+password+'@'+server+':'+port)+'#'+remark.replace(/↑/g,'').replace(/↓/g,'');
    },
    // ssr://server:port:protocol:method:obfs:password_base64/?params_base64
    ssr: function (data) {
        var server=data[order.address],
            port=data[order.port],
            protocol="origin",
            method=data[order.method],
            obfs="plain",
            password=data[order.password],
            remarks=data[order.globe]+'['+data[order.point]+']'+'('+date_str+data[order.clock]+')',
            group="ZnJlZS1zcw";
        return 'ssr://'+enb64(server+':'+port+':'+protocol+':'+method+':'+obfs+':'+enb64(password)+'/?remarks='+enb64(remarks.replace(/↑/g,'').replace(/↓/g,''))+'&group='+group);
    },
    // 将数据处理成链接
    datas: function () {
        var ssdatas;
        if (ss_table.rows('.selected').data().length > 0) {ssdatas = ss_table.rows('.selected').data();} else {ssdatas = ss_table.data();}
        ss_links_str = "";ssr_links_str = "";
        $.each(ssdatas, function(i, data){
            ss_links_str = ss_links_str + tools.ss(data) + '\n';
            ssr_links_str = ssr_links_str + tools.ssr(data) + '\n';
        });
        return ssdatas;
    },
    // 处理二维码事件
    qr: function (data) {
        var ss = tools.ss(data), ssr =tools.ssr(data);
        var qrcode = $('#qrcode0');
        qrcode.html('<div class="qr-link"><a href="">SS</a><input type="checkbox" name="ssqr" checked="checked"><br></div>'+
                    '<div class="qr-link"><a href="">SSR</a><input type="checkbox" name="ssrqr" checked="checked"><br></div>');
        qrcode.find('a').eq(0).attr('href', ss);
        qrcode.find('.qr-link').eq(0).qrcode({render:'canvas',background:'#FFFFFF',ecLevel:'M',label: 'SS link',text:ss});
        qrcode.find('a').eq(1).attr('href', ssr);
        qrcode.find('.qr-link').eq(1).qrcode({render:'canvas',background:'#FFFFFF',ecLevel:'M',label: 'SSR link',text:ssr});
        $('input:checkbox').click(function () {
            this.blur(); this.focus();
            $(this).siblings("canvas").eq(0).toggleClass('showoff');
        });
        layer.closeAll();
        layer.open({
            type:1,
            title:data[1]+':'+data[2]+' ('+data[6]+')',
            closeBtn:0,
            shade:0.1,
            area:'600px',
            shadeClose:true,
            content:qrcode
        });
    },
    upload: function () {
        function RssAlert(rss){
            layer.closeAll();
            layer.alert(
                '<a href="https://42one.fun/subconverter/">机场订阅转换工具--Subconverter</a><br><br>'+
                '㊣SSR:<a href="'+rss+'">'+rss+'</a><br>'+
                '⇲Clash:<a href="https://sub.42one.fun/sub?target=clash&url='+encodeURI(rss)+'">转换链接</a><br>',
                {title:'临时订阅地址'});
        }
        if (rss_url===undefined){
            GM_xmlhttpRequest({
                method:'POST',
                url: "https://paste.rs/",
                data: enb64(ssr_links_str),
                onloadstart: function() { layer.load(2, { time: 10000 }); },
                onload: function(response) {
                    rss_url=response.responseText;
                    GM_log(response);RssAlert(rss_url);
                    //window.open(response.responseText);
                },
                onerror: onload,
                ontimeout: onload
            });
        } else {RssAlert(rss_url);}
        return rss_url;
    }
};

document.onkeydown = function(e) { if (e.ctrlKey && 81 == e.keyCode) { tools.upload(); } }; // Ctrl+q

function make_area(){
    tools.area();
    $("#ss_area").html("");
    $("#ss_area").append("<option value=''></option>");
    for (var x=-1;x++,x<areas.length;){$('#ss_area').append("<option value='"+areas[x]+"'>"+country_code[areas[x]]+"</option>");}
}

function check_method(){
    ss_table.$('tr.selected').removeClass('selected');
    $(ss_id+ ' tbody').find('tr').each(function(){
        var m = $(this).find('td').eq(order.method).text();
        if (ok_method.indexOf(m)==-1) {$(this).toggleClass('selected');}
    });
    if (ss_table.rows('.selected').data().length > 0){
        layer.msg("已移除不兼容SSR的协议("+ss_table.rows('.selected').data().length+"条)", {time:1500});
        ss_table.rows('.selected').remove().draw();
    } else {
        layer.msg("所有链接协议可用", {time:1000});
    }
    $("#total").html("共 "+ ss_table.data().length+" 条");
}

function start(){
    layer.closeAll();
    $(".fa-info-circle").remove();
    site_info();
    ss_table = $(ss_id).DataTable( { retrieve: true } );
    unsafeWindow.ss_table = ss_table;
    order = tools.order();tools.datas();
    ss_table.order( [ 0, 'asc' ] ).draw();
    link_count = tools.datas().length;
    $("h3").eq(-1).append(
        "<small> <a title='"+GM_info.script.name+"' target='_blank' href='"+GM_info.script.supportURL+"'><i class='fa fa-bolt'></i> "+GM_info.script.version+"</a></small>"+
        "<small style='float:right'>"+
        "<button id='rss-upload' style='background-color:initial;'><i class='fa fa-rss'></i></button>"+
        "<button id='site_info' style='background-color:initial;'><i class='fa fa-question-circle'></i></button>"+
        "</small>"
    );$("title").append("⚡");
    $('#site_info').on('click',function(){site_info();});
    $('#rss-upload').on('click',function(){tools.upload();});
    $("#tools").html(
        "<li class='txt'>"+
        "<p id='link_num'><span id='total'>共 "+ss_table.data().length+" 条</span><span id='sel'></span></p></li>"+
        "<li class='btn'>"+
        "<button id='btn_clear' title='移除评分6以下'>移除不稳定</button>"+
        "<button id='btn_ss'>复制 SS</button>"+
        "<button id='btn_ssr'>复制SSR</button>"+
        "<select id='ss_area'></select>"+
        "</li>"
    );
    make_area();
    $('#ss_area').on('change', function(){
        $(ss_id+ ' tbody').find('tr').each(function(){
            if ($('#ss_area').val() == ''){
                $(this).removeClass('selected');
                $("#sel").html("");
            } else {
                if($(this).find('td').eq(order.globe).text().indexOf($('#ss_area').val()) != -1){
                    $(this).toggleClass('selected');
                } else {
                    $(this).removeClass('selected');
                }
                layer.msg("已选中 "+country_code[$('#ss_area').val()]+" 区域", {time:1000});
                $("#sel").html(",已选 "+ss_table.rows('.selected').data().length+" 条");
                ss_table.order( [ order.globe, 'asc' ] ).draw();
            }
        });
    });
    $('#btn_ss').on('click',function(){
        layer.msg("SS 链接复制成功("+tools.datas().length+"条)", {time:1000});
        GM_setClipboard(ss_links_str);});
    $('#btn_ssr').on('click',function(){
        layer.msg("SSR链接复制成功("+tools.datas().length+"条)", {time:1000});
        GM_setClipboard(ssr_links_str);});
    $('#btn_clear').on('click',function(){
        ss_table.$('tr.selected').removeClass('selected');
        $(ss_id+ ' tbody').find('tr').each(function(){
            var point_str = $(this).find('td').eq(0).text().replace(/↑/g,'').replace(/↓/g,'');
            var ping = point_str.split('/');
            if (point_str.match(/[a-zA-Z]/g)) {$(this).toggleClass('selected');}
            else if (ping.length) {for (var x in ping) {if (Number(ping[x])<=5){$(this).toggleClass('selected');break;} } } });
        if (ss_table.rows('.selected').data().length > 0){
            layer.msg("已移除不稳定链接("+ss_table.rows('.selected').data().length+"条)", {time:1000});
            $('#btn_clear').before("<small> (已移除"+ss_table.rows('.selected').data().length+"条) </small>");
            ss_table.rows('.selected').remove().draw();
        } else {
            layer.msg("所有链接较为稳定(>5)", {time:1000});
        }
        ss_table.order( [ 0, 'asc' ] ).draw();
        $("#total").html("共 "+ ss_table.data().length+" 条");
        $('#btn_clear').remove();
        make_area();
        method_clear();
    });
    ss_table.$('tr').click( function () {
        $(this).toggleClass('selected');
        if (ss_table.rows('.selected').data().length){
            $("#sel").html(",已选 "+ss_table.rows('.selected').data().length+" 条");
        } else {
            $("#sel").html("");
        }
    } );
    // 等待1s
    setTimeout(function(){
        $(ss_id+' tbody').off('click','i');
        $(ss_id+' tbody').on('click','i',function(){tools.qr(ss_table.row($(this).closest('tr')).data());});
    },1000);
}

function client_info(){
    $('#client').show();
    layer.open({
        type:1,
        title:false,
        closeBtn:0,
        shade:0.7,
        area: '500px',
        shadeClose:true,
        content:$('#client'),
        end : function(){$('#client').hide();}
    });
}

function site_info(){
    $('div.footer').show();
    layer.open({
        type:1,
        title:false,
        closeBtn:0,
        shade:0.7,
        area: '500px',
        shadeClose:true,
        content:$('div.footer'),
        end : function(){$('div.footer').hide();}
    });
}

function failed(){
    if (link_count === 0) {
        layer.confirm(ss_id+' 貌似脚本加载失败了!?<br>请等待页面加载,或者...', {
            title: GM_info.script.name+" "+GM_info.script.version,
            closeBtn: 0,
            shade: 0.5,
            shadeClose: true,
            resize: false,
            btn: ['刷新','反馈']
        }, function(){
            location.reload();
        }, function(){
            GM_openInTab(GM_info.script.supportURL);
        });
    }
}

function method_clear() {
    layer.confirm('是否移除不兼容SSR的协议?', {
        title: false,
        closeBtn: 0,
        shade: 0.5,
        shadeClose: true,
        resize: false,
        btn: ['确定','取消']
    }, function(index){
        check_method();
        layer.close(index);
    }, function(index){
        layer.close(index);
    });
}

unsafeWindow.tools = tools;
unsafeWindow.start = start;

$(document).ready(function() {
    $(ss_id).on('init.dt', function (){ start(); });
    setTimeout(function(){failed();},5000);
});