让你的sspanel可以订阅新版clash软件

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