Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support remote YAML parsers(part) of CFW #434

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Feature of [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta)
5. Build

```bash
./gradlew app:assembleMeta-AlphaRelease
./gradlew app:assembleAlphaRelease
```

### Automation
Expand Down
117 changes: 116 additions & 1 deletion core/src/main/golang/native/config/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/metacubex/mihomo/log"
"gopkg.in/yaml.v3"
"io"
"net/http"
U "net/url"
Expand Down Expand Up @@ -34,6 +36,36 @@ func openUrl(ctx context.Context, url string) (io.ReadCloser, error) {
return response.Body, nil
}

func openUrlAsString(ctx context.Context, url string) (string, error) {
body, requestErr := openUrl(ctx, url)

if requestErr != nil {
return "", requestErr
}

// 读取所有数据并转换为byte数组
data, err := io.ReadAll(body)
defer body.Close()
if err != nil {
return "", err
}
// 将数据转为字符串
content := string(data)
return content, nil
}

func openUrlAsYaml(ctx context.Context, url string) (map[string]interface{}, error) {
content, _ := openUrlAsString(ctx, url)
// 定义一个结构体来存储 YAML 解析结果
var config map[string]interface{} // 假设 config 是一个 map
// 解析 YAML 内容
err := yaml.Unmarshal([]byte(content), &config)
if err != nil {
return nil, err
}
return config, nil
}

func openContent(url string) (io.ReadCloser, error) {
return app.OpenContent(url)
}
Expand All @@ -60,6 +92,14 @@ func fetch(url *U.URL, file string) error {

defer reader.Close()

data, err := io.ReadAll(reader)
if err != nil {
return err
}
content := string(data)
parsedContent := applyParsers(ctx, content, url)
log.Debugln("最终subscribe:%s", parsedContent)

_ = os.MkdirAll(P.Dir(file), 0700)

f, err := os.OpenFile(file, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
Expand All @@ -69,14 +109,89 @@ func fetch(url *U.URL, file string) error {

defer f.Close()

_, err = io.Copy(f, reader)
_, err = f.WriteString(parsedContent)
if err != nil {
_ = os.Remove(file)
}

return err
}

func applyParsers(ctx context.Context, subscribeOriginalStr string, subscribeUrl *U.URL) string {
if !subscribeUrl.Query().Has("parsers") {
log.Debugln("需要处理parsers")
return subscribeOriginalStr
}

// 定义一个结构体来存储 YAML 解析结果
var subscribe map[string]interface{}

// 解析 YAML 内容
err := yaml.Unmarshal([]byte(subscribeOriginalStr), &subscribe)
if err != nil {
// 如果解析出错,返回错误信息作为字符串
log.Debugln("failed to parse YAML: %v", err)
return fmt.Sprintf("failed to parse YAML: %v", err)
}

var parsersUrl = subscribeUrl.Query().Get("parsers")
log.Debugln("找到parsersURL: %s", parsersUrl)
parsersContainerYml, parsersErr := openUrlAsYaml(ctx, parsersUrl)
if parsersErr != nil {
log.Debugln("拉取parsers失败: %v", parsersErr)
return subscribeOriginalStr
}

parsersContainer, parsersContainerExist := parsersContainerYml["parsers"].(map[string]interface{})
if !parsersContainerExist {
log.Debugln("parsers容器中不存在parsers节点")
return subscribeOriginalStr
}

parsers, parsersExist := parsersContainer["yaml"].(map[string]interface{})
if !parsersExist {
log.Debugln("parsers容器中不存在yaml节点")
return subscribeOriginalStr
}

subscribe = prependArr(subscribe, "proxies", parsers, "prepend-proxies")
subscribe = prependArr(subscribe, "proxy-groups", parsers, "prepend-proxy-groups")
subscribe = prependArr(subscribe, "rules", parsers, "prepend-rules")

// 将解析后的数据结构转回 YAML 格式的字符串
yamlBytes, err := yaml.Marshal(subscribe)
if err != nil {
log.Debugln("failed to marshal YAML: %v", err)
return fmt.Sprintf("failed to marshal YAML: %v", err)
}

// 返回解析后的 YAML 字符串
return string(yamlBytes)
}

func prependArr(subscribe map[string]interface{}, subscribeKey string, parsers map[string]interface{}, parserKey string) map[string]interface{} {
// 处理prepend-rules
if arrToPrepend, arrToPrependExist := parsers[parserKey].([]interface{}); arrToPrependExist {
log.Debugln("parses找到%s", parserKey)
// 提取 originalArr 字段
if originalArr, originalArrExist := subscribe[subscribeKey].([]interface{}); originalArrExist {
log.Debugln("subscribe找到%s", subscribeKey)
// 将新的规则添加到 originalArr 数组的头部
log.Debugln("subscribe原始%s:%v", subscribeKey, originalArr)
originalArr = append(arrToPrepend, originalArr...)
// 更新 subscribe 中的 originalArr 字段
subscribe[subscribeKey] = originalArr
log.Debugln("subscribe编辑后%s:%v", subscribeKey, subscribe[subscribeKey])
} else {
subscribe[subscribeKey] = arrToPrepend
log.Debugln("subscribe编辑后%s:%v", subscribeKey, subscribe[subscribeKey])
}
} else {
log.Debugln("parses未找到%s", parserKey)
}
return subscribe
}

func FetchAndValid(
path string,
url string,
Expand Down