Last active
September 18, 2024 02:37
-
-
Save CaiJingLong/20e9cd43e1816dedf71caca8566d1a52 to your computer and use it in GitHub Desktop.
一个转化订阅的 dart 服务器,处理 clash 的 json,添加规则/节点
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"port": 3300, | |
"meta": "https://example.com/api/v1/client/subscribe?token=REDACTED&flag=clash", | |
"ssr-meta": "https://example.com?clash=1", | |
"nodes": [ | |
{ | |
"name": "my-home", | |
"schema": "hysteria2", | |
"host": "REDACTED", | |
"port": 34443, | |
"password": "REDACTED" | |
} | |
], | |
"rules": [ | |
"GEOIP,CN,DIRECT" | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:convert'; | |
import 'dart:io'; | |
import 'package:http/http.dart' as http; | |
import 'package:yaml/yaml.dart'; | |
import 'package:shelf/shelf.dart' as shelf; | |
import 'package:shelf/shelf_io.dart' as io; | |
import 'package:shelf_router/shelf_router.dart'; | |
const String configFile = "config.json"; // 指定配置文件的地址 config.json | |
Future<Map<String, dynamic>> readConfig() async { | |
final file = File(configFile); | |
final jsonString = await file.readAsString(); | |
return json.decode(jsonString); | |
} | |
// 节点 | |
class Node { | |
String name; | |
String schema; | |
String host; | |
int port; | |
String password; | |
Node(this.name, this.schema, this.host, this.port, this.password); | |
static List<Node> listFromConfig(List<dynamic> param) { | |
return param | |
.map((node) => Node(node['name'], node['schema'], node['host'], | |
node['port'], node['password'])) | |
.toList(); | |
} | |
} | |
abstract class Injector { | |
void injectNode(Node node); | |
String output(); | |
} | |
class ClashInjector implements Injector { | |
late Map<String, dynamic> src; | |
ClashInjector(Map<String, dynamic> map) { | |
src = Map.from(map); | |
} | |
@override | |
void injectNode(Node node) async { | |
final nodeDict = { | |
"name": node.name, | |
"type": node.schema, | |
"server": node.host, | |
"port": node.port, | |
"password": node.password, | |
"skip-cert-verify": true, | |
"sni": node.host, | |
}; | |
final proxies = src['proxies'].toList(); | |
proxies.add(nodeDict); | |
src['proxies'] = proxies; | |
final proxyGroups = src['proxy-groups'].toList(); | |
proxyGroups.add({ | |
"name": "${node.name}_group", | |
"type": "select", | |
"proxies": [node.name], | |
}); | |
src['proxy-groups'] = proxyGroups; | |
} | |
void injectRule(List<String> rules) { | |
final newRules = [...rules, ...src['rules']]; | |
src['rules'] = newRules; | |
} | |
@override | |
String output() { | |
return json.encode(src); | |
} | |
} | |
// 添加服务器路由处理 | |
class Server { | |
Router router = Router(); | |
Server() { | |
router.get('/meta', _handleMeta); | |
router.get('/ssr-meta', _handleSsrMeta); | |
} | |
Future<shelf.Response> _handleMeta(shelf.Request request) async { | |
return _clashHandle('meta'); | |
} | |
Future<shelf.Response> _handleSsrMeta(shelf.Request request) async { | |
return _clashHandle('ssr-meta'); | |
} | |
Future<shelf.Response> _clashHandle(String nodeName) async { | |
final config = await readConfig(); | |
final metaUrl = config[nodeName]; | |
final response = await http.get(Uri.parse(metaUrl)); | |
final content = response.body; | |
final YamlMap d = loadYaml(content); | |
final injector = ClashInjector(d.cast()); | |
final nodes = Node.listFromConfig(config['nodes']); | |
for (var node in nodes) { | |
injector.injectNode(node); | |
} | |
injector.injectRule(config['rules'].cast<String>()); | |
final res = injector.output(); | |
return shelf.Response.ok( | |
res, | |
headers: {'Content-Type': 'application/json; charset=utf-8'}, | |
); | |
} | |
} | |
void main() async { | |
final config = await readConfig(); | |
final port = config['port']; | |
final server = Server(); | |
final handler = const shelf.Pipeline() | |
.addMiddleware(shelf.logRequests()) | |
.addHandler(server.router); | |
final httpServer = await io.serve(handler, '0.0.0.0', port); | |
print('服务器运行在 http://${httpServer.address.host}:${httpServer.port}'); | |
print('订阅链接: http://${httpServer.address.host}:${httpServer.port}/meta'); | |
print('订阅链接: http://${httpServer.address.host}:${httpServer.port}/ssr-meta'); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: clash_subscription_injector | |
description: A proxy tools for clash, to inject nodes and rules into subscription | |
version: 1.0.0+1 | |
environment: | |
sdk: ">=2.12.0 <3.0.0" | |
dependencies: | |
http: ^0.13.3 | |
shelf_router: ^1.1.4 | |
yaml: ^3.1.0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment