12月25日:对本篇文章有遗漏、出错的地方进行修复,并增加新旧版本订阅兼容的探讨问题。

随着clash内核升级到1.1并支持ssr以后,Windows版、MacOS版和Android版陆续跟进并支持ssr,从此带R版本的clash就退出历史舞台。略觉得美中不足的地方就是支持ssr后的clash内核不再向下兼容以前版本的参数,据作者所说是为了减少开发成本。那么如何让sspanel的订阅链接支持新版clash就是大家慢慢开始讨论的话题了,更甚者是让同一订阅链接支持新旧版clash的想法。

新版clash软件使用方法:
https://www.mebi.me/1609.html

前提

  • 修改之前务必先备份
  • 有一定动手能力
  • 有php面向对象的基础

订阅支持新版clash

注意:sspanel一直在保持活跃更新,相隔几个版本可能代码相差就很大,因此如果你的版本与本站演示版本不同,下面就仅做参考用。如果你的网站根目录下有 app/Utils/AppURI.php 文件,那么应该跟演示版本差别不大。

新增配置模板

clash配置模板在网站根目录下的 resources/conf/clash.tpl,复制一份 clash.tpl,重命名为 new_clash.tpl并打开,定位59行:

Proxy:

修改为:

proxies:

定位到64行:

Proxy Group:

修改为:

proxy-groups:

新增规则文件

clash规则文件在网站根目录下的 resources/conf/rule 目录里,复制一份 lhie1_Rule.yaml,重命名为 lhie1_New_Rule.yaml,打开 lhie1_New_Rule.yaml 文件定位到第一行:

Rule:

修改为:

rules:

新增节点参数模板

新版clash把ssr的obfsparam和protocolparam两个参数改成了obfs-param和protocol-param。修改 /app/Utils/AppURI.php,找到名为 getClashURI 的函数,复制粘贴此函数到它的下面,命名为 getNewClashURI,getNewClashURI 函数和 getClashRUI 函数唯一需要修改地方如下:

$return = [
    'name' => $item['remark'],
    'type' => 'ssr',
    'server' => $item['address'],
    'port' => $item['port'],
    'cipher' => $item['method'],
    'password' => $item['passwd'],
    'protocol' => $item['protocol'],
    'protocolparam' => $item['protocol_param'],
    'obfs' => $item['obfs'],
    'obfsparam' => $item['obfs_param']
];
break;

修改为:

$return = [
     'name' => $item['remark'],
     'type' => 'ssr', 'server' => $item['address'],
     'port' => $item['port'],
     'cipher' => $item['method'],
     'password' => $item['passwd'],
     'protocol' => $item['protocol'],
     'protocol-param' => $item['protocol_param'],
     'obfs' => $item['obfs'],
     'obfs-param' => $item['obfs_param']
];
break;

修改控制配置函数

文件 app/Controllers/ConfController.php,复制 getClashConfs 函数重命名为 getNewClashConfs 函数,代码几乎一致,区别在$tmp的键Proxy Group和Proxy改为proxy-groups和Proxies。

public static function getNewClashConfs($User, $AllProxys, $Configs, $local = false)
{
    if (isset($Configs['Proxy']) || count($Configs['Proxy']) != 0) {
        $tmpProxys = array_merge($AllProxys, $Configs['Proxy']);
    } else {
        $tmpProxys = $AllProxys;
    }
    $Proxys = [];
    foreach ($tmpProxys as $Proxy) {
        unset($Proxy['class']);
        $Proxys[] = $Proxy;
    }
    $tmp = self::getClashConfGeneral($Configs['General']);
    $tmp['Proxies'] = $Proxys;
    if (isset($Configs['ProxyGroup'])) {
        $tmp['proxy-groups'] = self::getClashConfProxyGroup(
            $AllProxys,
            $Configs['ProxyGroup']
        );
    } else {
        $tmp['proxy-groups'] = self::getClashConfProxyGroup(
            $AllProxys,
            $Configs['Proxy Group']
        );
    }
    $Conf = '#!MANAGED-CONFIG '
        . Config::get('baseUrl') . $_SERVER['REQUEST_URI'] .
        "\n\n#---------------------------------------------------#" .
        "\n## 上次更新于:" . date("Y-m-d h:i:s") .
        "\n#---------------------------------------------------#" .
        "\n\n"
        . Yaml::dump($tmp, 4, 2) .
        "\n\n"
        . self::getClashConfRule($Configs['Rule'], $local);

    return $Conf;
}

修改控制文件

打开 app/Controllers/LinkController.php 文件,找到名为 getClash 的函数,首先我们判断订阅链接是否带有new=1的参数,如果有表示请求新版clash订阅链接,用上面添加的 getNewClashURI 函数获取节点的参数和信息,无则表示请求旧版clash订阅链接,使用原来的 getClashURI 函数获取节点的参数和信息。

getClash函数中找到如下代码

foreach ($items as $item) {
    $Proxy = AppURI::getClashURI($item, $ssr_support);
    if ($Proxy !== null) {
        if (isset($opts['source']) && $opts['source'] != '') {
            $Proxy['class'] = $item['class'];
        }
        $Proxys[] = $Proxy;
    }
}

修改为

if (isset($opts['new']) && $opts['new'] == 1)
{
    foreach ($items as $item) {
        $Proxy = AppURI::getNewClashURI($item, true);
        if ($Proxy !== null) {
            if (isset($opts['source']) && $opts['source'] != '') {
                $Proxy['class'] = $item['class'];
            }
            $Proxys[] = $Proxy;
        }
 }
 }else{
    foreach ($items as $item) {
        $Proxy = AppURI::getClashURI($item, $ssr_support);
        if ($Proxy !== null) {
            if (isset($opts['source']) && $opts['source'] != '') {
                $Proxy['class'] = $item['class'];
            }
            $Proxys[] = $Proxy;
        }
    }
}

找到

return ConfController::getClashConfs(
    $user,
    $Proxys,
    $Content
);

修改为:

if (isset($opts['new']) && $opts['new'] == 1)
{
    return ConfController::getNewClashConfs(
        $user,
        $Proxys,
        $Content
    );
}else{
    return ConfController::getClashConfs(
        $user,
        $Proxys,
        $Content
    );
}

找到:

return $render->fetch('clash.tpl');

修改为:

if (isset($opts['new']) && $opts['new'] == 1)
{
    return $render->fetch('new_clash.tpl');
}else{
    return $render->fetch('clash.tpl');
}

好了完工。在原来的clash订阅链接最后加上参数:&new=1,此链接可适用于新版clash。如果不加参数就适用旧版clash。

兼容新旧版clash

两个订阅其实用户还是有一定选择困难的,几乎没人知道自己的clash是否是“新版”。那么使用同一订阅链接兼容新旧版clash会有更好的适用性。
还是在 LinkController.php 文件的getClash函数,我们需要判断clash的user-agent返回的版本号来确定用户此时正在使用的是旧版还是新版clash,并给出相应的订阅内容。
另外还需要清楚新旧版的版本号“分界线”:ClashforWindows是0.11.2;ClashForAndroid是2.1.1;ClashX是1.30.0;linux判断没有意义。ClashforWindows的user-agent特殊,早期版本没有ClashforWindows,然后到0.11.0版本之前一直是ClashforWindows/7.0,0.11.0版本才开始有正式格式:ClashforWindows/version,所以我们要注意判断。

//兼容新旧版clash
$clash_new = false;
$agent_arr = explode('/', $request->getHeaderLine('User-Agent'));
$clash_agent = $agent_arr[0];
$clash_version = explode(' ', $agent_arr[1])[0];
if (strstr($clash_agent, 'ClashforWindows'))
{
     if ($clash_version >= "0.11.2" && $clash_version != "7.0")
     {
          $clash_new = true;
     }
}
if (strstr($clash_agent, 'ClashForAndroid'))
{
    if ($clash_version >= "2.1.1")
    {
         $clash_new = true;
    }
}
if (strstr($clash_agent, 'ClashX'))
{
    if ($clash_version >= "1.30.0")
    {
        $clash_new = true;
    }
 }

这段代码需要跟上面的搭配,但是不需要参数new了,自己适当修改修改。这段代码引入了函数参数$request,原来代码中这个参数在函数间不传递,那么我们就要增加这个参数。
找到GetContent函数,这是一个订阅的入口函数。找到如下代码

$content = self::$class($user, $query_value, $opts, $Rule);

此代码一共有两处,都需要修改。修改为

$content = self::$class($user, $query_value, $request, $opts, $Rule);

然后把对应的函数的参数也加上$request。例如我们修改的getClash函数,原先为

public static function getClash($user, $clash, $opts, $Rule)

修改为

public static function getClash($user, $clash, $request, $opts, $Rule)

除了getClash函数外,还有getSSD、getShadowrocket等等。这些都必须修改,否则会因为传递参数数量不匹配,网页直接500错误。

如不会修改或者你的版本与演示版本不匹配,欢迎付费找我:tg@mebier

Last modification:December 26th, 2020 at 11:05 am
如果觉得我的文章对你有用,请随意赞赏