mirror of
https://github.com/v2fly/domain-list-community.git
synced 2026-02-04 13:03:14 +07:00
* main.go: improve code * main.go: move refMap from global variable to local * main.go: allow tld to be a parent domain * datdump: improve code
170 lines
4.6 KiB
Go
170 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/v2fly/domain-list-community/internal/dlc"
|
|
router "github.com/v2fly/v2ray-core/v5/app/router/routercommon"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var (
|
|
inputData = flag.String("inputdata", "dlc.dat", "Name of the geosite dat file")
|
|
outputDir = flag.String("outputdir", "./", "Directory to place all generated files")
|
|
exportLists = flag.String("exportlists", "", "Lists to be exported, separated by ',' (empty for _all_)")
|
|
)
|
|
|
|
type DomainRule struct {
|
|
Type string
|
|
Value string
|
|
Attrs []string
|
|
}
|
|
|
|
type DomainList struct {
|
|
Name string
|
|
Rules []DomainRule
|
|
}
|
|
|
|
func (d *DomainRule) domain2String() string {
|
|
var dstr strings.Builder
|
|
dstr.Grow(len(d.Type) + len(d.Value) + 10)
|
|
fmt.Fprintf(&dstr, "%s:%s", d.Type, d.Value)
|
|
if len(d.Attrs) != 0 {
|
|
fmt.Fprintf(&dstr, ":@%s", strings.Join(d.Attrs, ",@"))
|
|
}
|
|
return dstr.String()
|
|
}
|
|
|
|
func loadGeosite(path string) ([]DomainList, map[string]*DomainList, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to read geosite file: %w", err)
|
|
}
|
|
vgeositeList := new(router.GeoSiteList)
|
|
if err := proto.Unmarshal(data, vgeositeList); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to unmarshal: %w", err)
|
|
}
|
|
domainLists := make([]DomainList, len(vgeositeList.Entry))
|
|
domainListByName := make(map[string]*DomainList, len(vgeositeList.Entry))
|
|
for i, vsite := range vgeositeList.Entry {
|
|
rules := make([]DomainRule, 0, len(vsite.Domain))
|
|
for _, vdomain := range vsite.Domain {
|
|
rule := DomainRule{Value: vdomain.Value}
|
|
switch vdomain.Type {
|
|
case router.Domain_RootDomain:
|
|
rule.Type = dlc.RuleTypeDomain
|
|
case router.Domain_Regex:
|
|
rule.Type = dlc.RuleTypeRegexp
|
|
case router.Domain_Plain:
|
|
rule.Type = dlc.RuleTypeKeyword
|
|
case router.Domain_Full:
|
|
rule.Type = dlc.RuleTypeFullDomain
|
|
default:
|
|
return nil, nil, fmt.Errorf("invalid rule type: %+v", vdomain.Type)
|
|
}
|
|
for _, vattr := range vdomain.Attribute {
|
|
rule.Attrs = append(rule.Attrs, vattr.Key)
|
|
}
|
|
rules = append(rules, rule)
|
|
}
|
|
domainLists[i] = DomainList{
|
|
Name: strings.ToUpper(vsite.CountryCode),
|
|
Rules: rules,
|
|
}
|
|
domainListByName[domainLists[i].Name] = &domainLists[i]
|
|
}
|
|
return domainLists, domainListByName, nil
|
|
}
|
|
|
|
func exportSite(name string, domainListByName map[string]*DomainList) error {
|
|
domainList, ok := domainListByName[strings.ToUpper(name)]
|
|
if !ok {
|
|
return fmt.Errorf("list %q does not exist", name)
|
|
}
|
|
if len(domainList.Rules) == 0 {
|
|
return fmt.Errorf("list %q is empty", name)
|
|
}
|
|
file, err := os.Create(filepath.Join(*outputDir, name+".yml"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
w := bufio.NewWriter(file)
|
|
fmt.Fprintf(w, "%s:\n", name)
|
|
for _, domain := range domainList.Rules {
|
|
fmt.Fprintf(w, " - %q\n", domain.domain2String())
|
|
}
|
|
return w.Flush()
|
|
}
|
|
|
|
func exportAll(filename string, domainLists []DomainList) error {
|
|
file, err := os.Create(filepath.Join(*outputDir, filename))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
w := bufio.NewWriter(file)
|
|
w.WriteString("lists:\n")
|
|
for _, domainList := range domainLists {
|
|
fmt.Fprintf(w, " - name: %s\n", strings.ToLower(domainList.Name))
|
|
fmt.Fprintf(w, " length: %d\n", len(domainList.Rules))
|
|
w.WriteString(" rules:\n")
|
|
for _, domain := range domainList.Rules {
|
|
fmt.Fprintf(w, " - %q\n", domain.domain2String())
|
|
}
|
|
}
|
|
return w.Flush()
|
|
}
|
|
|
|
func run() error {
|
|
// Make sure output directory exists
|
|
if err := os.MkdirAll(*outputDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create output directory: %w", err)
|
|
}
|
|
|
|
fmt.Printf("loading source data %q...\n", *inputData)
|
|
domainLists, domainListByName, err := loadGeosite(*inputData)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to loadGeosite: %w", err)
|
|
}
|
|
|
|
var exportListSlice []string
|
|
for raw := range strings.SplitSeq(*exportLists, ",") {
|
|
if trimmed := strings.TrimSpace(raw); trimmed != "" {
|
|
exportListSlice = append(exportListSlice, trimmed)
|
|
}
|
|
}
|
|
if len(exportListSlice) == 0 {
|
|
exportListSlice = []string{"_all_"}
|
|
}
|
|
|
|
for _, eplistname := range exportListSlice {
|
|
if strings.EqualFold(eplistname, "_all_") {
|
|
if err := exportAll(filepath.Base(*inputData)+"_plain.yml", domainLists); err != nil {
|
|
fmt.Printf("failed to exportAll: %v\n", err)
|
|
continue
|
|
}
|
|
} else {
|
|
if err := exportSite(eplistname, domainListByName); err != nil {
|
|
fmt.Printf("failed to exportSite: %v\n", err)
|
|
continue
|
|
}
|
|
}
|
|
fmt.Printf("list: %q has been exported successfully.\n", eplistname)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
if err := run(); err != nil {
|
|
fmt.Printf("Fatal error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|