Compare commits

...

16 Commits

Author SHA1 Message Date
MkQtS
3747b46453 xiaomi: add more domains (#3071)
* Add xiaomi-ai

* category-dev-cn: add openvela.com
2025-12-17 12:35:10 +08:00
Zhong Lufan (钟路帆)
fecad9d4c0 category-game-platforms-download: add cache9-hkg1.steamcontent.com (#3070) 2025-12-17 12:08:07 +08:00
rootmelo92118
8cd2762ca2 Update thescoregroup (#3054) 2025-12-17 01:35:23 +08:00
Loyalsoldier
2a6d6fb120 Docs: fix lint and link (#3069) 2025-12-17 01:20:21 +08:00
Loyalsoldier
a4da81130d Chore: update dependencies (#3068) 2025-12-17 01:05:10 +08:00
Loyalsoldier
4d45b17cd8 Chore: refine code (#3067) 2025-12-17 00:45:09 +08:00
Loyalsoldier
d8bd29ce92 Fix: incorrect lowercase for regexp rules (#3066) 2025-12-17 00:17:43 +08:00
Loyalsoldier
72eb885658 Refine: extract rule type (#3065) 2025-12-17 00:10:33 +08:00
风扇滑翔翼
93bfcfd142 Feat: check regexp before build (#3064)
Co-authored-by: Loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com>
2025-12-16 23:40:25 +08:00
DeepChirp
cbe19f0562 picacg-ads: split from picacg 2025-12-16 21:42:59 +08:00
DeepChirp
ea99bef4a2 google-ads: comment out problematic rule 2025-12-16 21:42:59 +08:00
深鸣
9b01521761 ctyun: add new entry (#3059) 2025-12-16 21:24:37 +08:00
深鸣
8d50851b58 umeng-ads: add more domains (#3060) 2025-12-16 21:13:11 +08:00
sklimoff
16923730a4 Add wink and include it in category-ru (#3058) 2025-12-16 21:06:32 +08:00
Luo Chen
7e3137a0d1 Fix #3055 (#3056) 2025-12-16 09:54:53 +08:00
tooadstool
6b4c09860b Add Ookla Speedtest Central to the list (#3053)
* Add Ookla Speedtest Central to the list

* Update ookla-speedtest

---------

Co-authored-by: rootmelo92118 <32770959+rootmelo92118@users.noreply.github.com>
2025-12-15 22:40:47 +08:00
20 changed files with 199 additions and 58 deletions

View File

@@ -94,13 +94,13 @@ full:www.google.com
> The following types of rules are **NOT** fully compatible with the ones that defined by user in V2Ray config file. Do **Not** copy and paste directly.
* Comment begins with `#`. It may begin anywhere in the file. The content in the line after `#` is treated as comment and ignored in production.
* Inclusion begins with `include:`, followed by the file name of an existing file in the same directory.
* Subdomain begins with `domain:`, followed by a valid domain name. The prefix `domain:` may be omitted.
* Keyword begins with `keyword:`, followed by a string.
* Regular expression begins with `regexp:`, followed by a valid regular expression (per Golang's standard).
* Full domain begins with `full:`, followed by a complete and valid domain name.
* Domains (including `domain`, `keyword`, `regexp` and `full`) may have one or more attributes. Each attribute begins with `@` and followed by the name of the attribute.
- Comment begins with `#`. It may begin anywhere in the file. The content in the line after `#` is treated as comment and ignored in production.
- Inclusion begins with `include:`, followed by the file name of an existing file in the same directory.
- Subdomain begins with `domain:`, followed by a valid domain name. The prefix `domain:` may be omitted.
- Keyword begins with `keyword:`, followed by a string.
- Regular expression begins with `regexp:`, followed by a valid regular expression (per Golang's standard).
- Full domain begins with `full:`, followed by a complete and valid domain name.
- Domains (including `domain`, `keyword`, `regexp` and `full`) may have one or more attributes. Each attribute begins with `@` and followed by the name of the attribute.
> **Note:** Adding new `regexp` and `keyword` rules is discouraged because it is easy to use them incorrectly, and proxy software cannot efficiently match these types of rules.
@@ -113,10 +113,10 @@ To generate a section:
1. Remove all the comments in the file.
2. Replace `include:` lines with the actual content of the file.
3. Omit all empty lines.
4. Generate each `domain:` line into a [sub-domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/config.proto#L21).
5. Generate each `keyword:` line into a [plain domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/config.proto#L17).
6. Generate each `regexp:` line into a [regex domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/config.proto#L19).
7. Generate each `full:` line into a [full domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/config.proto#L23).
4. Generate each `domain:` line into a [sub-domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L21).
5. Generate each `full:` line into a [full domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L23).
6. Generate each `keyword:` line into a [plain domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L17).
7. Generate each `regexp:` line into a [regex domain routing rule](https://github.com/v2fly/v2ray-core/blob/master/app/router/routercommon/common.proto#L19).
## How to organize domains
@@ -130,8 +130,8 @@ Attribute is useful for sub-group of domains, especially for filtering purpose.
## Contribution guideline
* Fork this repo, make modifications to your own repo, file a PR.
* Please begin with small size PRs, say modification in a single file.
* A PR must be reviewed and approved by another member.
* A script will verify your pull request to test whether your PR is correct or not every time you update the PR. Only the PR which passes the test will be merged. Please go to the Action label to get detailed information if you didn't pass it. We also provide the file which has been generated to make you test.
* After a few successful PRs, you may apply for manager access to this repository.
- Fork this repo, make modifications to your own repo, file a PR.
- Please begin with small size PRs, say modification in a single file.
- A PR must be reviewed and approved by another member.
- A script will verify your pull request to test whether your PR is correct or not every time you update the PR. Only the PR which passes the test will be merged. Please go to the Action label to get detailed information if you didn't pass it. We also provide the file which has been generated to make you test.
- After a few successful PRs, you may apply for manager access to this repository.

View File

@@ -38,6 +38,7 @@ include:newrelic-ads
include:ogury-ads
include:ookla-speedtest-ads
include:openx-ads
include:picacg-ads
include:pocoiq-ads
include:pubmatic-ads
include:qihoo360-ads

View File

@@ -1,5 +1,7 @@
# DeepSeek
include:deepseek
include:doubao
include:trae # MarsCode
include:xiaomi-ai
# RWKV
rwkv.cn
@@ -19,10 +21,6 @@ yiyan.baidu.com
# 天工AI
tiangong.cn
# 字节跳动豆包、Trae(MarsCode)
include:doubao
include:trae
# 智谱清言
bigmodel.cn
chatglm.cn

View File

@@ -20,4 +20,5 @@ include:tencent-dev
include:ubuntukylin
jinrishici.com
openvela.com
tipdm.org

View File

@@ -362,6 +362,7 @@ full:cache8-waw1.steamcontent.com
full:cache9-atl3.steamcontent.com
full:cache9-fra1.steamcontent.com
full:cache9-fra2.steamcontent.com
full:cache9-hkg1.steamcontent.com
full:cache9-iad1.steamcontent.com
full:cache9-lax1.steamcontent.com
full:cache9-lhr1.steamcontent.com

View File

@@ -23,5 +23,6 @@ include:mailru-group
include:okko
include:ozon
include:rutube
include:wink
include:x5
include:yandex

69
data/ctyun Normal file
View File

@@ -0,0 +1,69 @@
# 天翼云
# 京ICP备2021034386号
ctadns.cn
bjctyiptv.cn
cqctyiptv.cn
ctacdn.cn
ctaigw.cn
ctbcdn.com
ctcdn.cn
ctcdn.com.cn
ctcdnov.net
ctcloudzos.cn
ctcns.cn
ctdcdn.com
ctdns.cn
ctdns.com.cn
ctdns.net
ctecdn.cn
ctecx.cn
ctgcdn.com
cthcdn.cn
cthcdn.com
cthcdn.net
ctlcdn.cn
ctlcdn.com
ctlcdn.net
ctmcdn.cn
ctovcdn.com
ctrender.com
ctwcdn.cn
ctxcdn.cn
ctxcdn.com
ctxcdn.net
ctxirang.cn
ctxirang.com
ctycdn.cn
ctycdn.net
ctycdn.net.cn
ctydoh.cn
ctyecx.cn
ctyiptv.cn
ctyun.cn
ctyun.com.cn
ctyuncdn.cn
ctyuncs.cn
ctyuninner.com
ctyunmds.cn
ctyunwaf.cn
ctyunwaf.com
ctyunwaf1.com
ctyunwaf3.cn
ctyunxs.cn
ctyunzos.cn
ctzcdn.cn
ctzcdn.com
edgecloudx.cn
faasapp.cn
faasdev.cn
fjctyiptv.cn
gdctyiptv.cn
gsctyiptv.cn
gsjtyiptv.cn
gzctyiptv.cn
jsctyiptv.cn
modelers.cn
scctyiptv.cn
snctyiptv.cn
ynctyiptv.cn
ynjtyiptv.cn

View File

@@ -54,6 +54,7 @@ include:aws-cn
include:baishancloud
include:bootcdn
include:cloudflare-cn
include:ctyun
include:dwion
include:maocloud
include:qingcloud

View File

@@ -49,6 +49,7 @@ partnerad.l.google.com @ads
urchin.com @ads
full:analytics.google.com @ads
full:fundingchoicesmessages.google.com @ads
# https://github.com/AdguardTeam/FiltersRegistry/pull/1154
# full:fundingchoicesmessages.google.com @ads
regexp:^adservice\.google\.([a-z]{2}|com?)(\.[a-z]{2})?$ @ads

View File

@@ -13,3 +13,6 @@ webtest.net
full:www.speedtest.net.cdn.cloudflare.net
include:ookla-speedtest-ads
# SpeedTest Node
ookla-speedtest-central.hgconair.hgc.com.hk # HGC Global Communications 香港環電

View File

@@ -1,3 +1,5 @@
include:picacg-ads
bikaa.xyz
bikac.xyz
bikaios.xyz
@@ -10,6 +12,5 @@ picacomic.xyz
wikawika.xyz
# Image Resource Domain like `img.diwodiwo.xyz` `s3.diwodiwo.xyz` `storage.diwodiwo.xyz` `storage-b.diwodiwo.xyz`
regexp:^([a-z0-9-]+\.)*(?!ad-display\.|ad-channel\.)[a-z0-9-]+\.diwodiwo\.xyz$
ad-channel.diwodiwo.xyz @ads
ad-display.diwodiwo.xyz @ads
diwodiwo.xyz

2
data/picacg-ads Normal file
View File

@@ -0,0 +1,2 @@
full:ad-channel.diwodiwo.xyz @ads
full:ad-display.diwodiwo.xyz @ads

View File

@@ -4,13 +4,18 @@
60plusmilfs.com
analqts.com
ashleysageellison.com
asiancoochies.com
autumn-jade.com
beascoremodel.com
bigboobalexya.com
bigboobbundle.com
bigboobdaria.com
bigboobspov.com
bigboobvanessay.com
bigtitangelawhite.com
bigtithitomi.com
bigtithooker.com
bigtitkatiethornton.com
bigtitterrynova.com
bigtitvenera.com
blackandstacked.com
@@ -23,11 +28,13 @@ bustydustystash.com
bustyinescudna.com
bustykellykay.com
bustykerrymarie.com
bustylezzies.com
bustylornamorgan.com
bustymerilyn.com
bustyoldsluts.com
bustysammieblack.com
cherrybrady.com
chicksonblackdicks.com
chloesworld.com
christymarks.com
cock4stepmom.com
@@ -37,6 +44,7 @@ crystalgunnsworld.com
daylenerio.com
desiraesworld.com
dianepoppos.com
ebonythots.com
eboobstore.com
evanottyvideos.com
feedherfuckher.com
@@ -45,14 +53,19 @@ getscorecash.com
grannygetsafacial.com
grannylovesbbc.com
grannylovesyoungcock.com
hairycoochies.com
homealonemilfs.com
hornyasianmilfs.com
ibonedyourmom.com
ifuckedtheboss.com
jessicaturner.co.uk
jessicaturner.com
joanabliss.com
juliamiles.com
karinahart.com
karlajames.com
latinacoochies.com
latinmommas.com
leannecrowvideos.com
legsex.com
linseysworld.com
@@ -73,6 +86,7 @@ pickinguppussy.com
pornloser.com
pornmegaload.com
reneerossvideos.com
roxired.com
sarennasworld.com
scoreclassics.com
scoregroup.com

View File

@@ -1,6 +1,24 @@
aaid.umeng.com @ads
alog.umeng.com @ads
alog.umengcloud.com @ads
alogs.umeng.com @ads
alogus.umeng.com @ads
ar.umeng.com @ads
aspect-upush.umeng.com @ads
audid.umeng.com @ads
ccs.umeng.com @ads
cnlogs.umeng.com @ads
cnlogs.umengcloud.com @ads
# https://github.com/TG-Twilight/AWAvenue-Ads-Rule/issues/185
# errlog.umeng.com @ads
# errnewlog.umeng.com @ads
new-aaid.umeng.com @ads
new-aaid.umeng.com.gds.alibabadns.com @ads
oc.umeng.com @ads
plbslog.umeng.com @ads
resolve.umeng.com @ads
ulogs.umeng.com @ads
ulogs.umengcloud.com @ads
utoken.umeng.com @ads
# CNZZ

3
data/wink Normal file
View File

@@ -0,0 +1,3 @@
ngenix.net
restream-media.net
wink.ru

View File

@@ -1,4 +1,5 @@
include:xiaomi-ads
include:xiaomi-ai
mgslb.com
mi-idc.com

3
data/xiaomi-ai Normal file
View File

@@ -0,0 +1,3 @@
miaibox.com
xiaoaiassist.com
xiaomimimo.com

8
go.mod
View File

@@ -1,14 +1,14 @@
module github.com/v2fly/domain-list-community
go 1.24
go 1.24.0
require (
github.com/v2fly/v2ray-core/v5 v5.38.0
google.golang.org/protobuf v1.36.8
github.com/v2fly/v2ray-core/v5 v5.42.0
google.golang.org/protobuf v1.36.11
)
require (
github.com/adrg/xdg v0.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/sys v0.38.0 // indirect
)

16
go.sum
View File

@@ -8,13 +8,13 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/v2fly/v2ray-core/v5 v5.38.0 h1:DjpBP9dyzpFfCNGg+C6FnEkFDT3pfe29I0leYtNbKNY=
github.com/v2fly/v2ray-core/v5 v5.38.0/go.mod h1:PHM3drDx9mbme6xE8fRZT979wNPHWCbCIPreAIqxqUw=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/v2fly/v2ray-core/v5 v5.42.0 h1:lyJrN3BDmu7lnVeMIlonAct8TSSHpIrNYP6/uYAbIBk=
github.com/v2fly/v2ray-core/v5 v5.42.0/go.mod h1:TyECxvulzqeaiFK14qNwhcoYOGnVmBkAj3Bs2MkVrNU=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

65
main.go
View File

@@ -2,11 +2,11 @@ package main
import (
"bufio"
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
@@ -22,6 +22,14 @@ var (
exportLists = flag.String("exportlists", "", "Lists to be flattened and exported in plaintext format, separated by ',' comma")
)
const (
RuleTypeDomain string = "domain"
RuleTypeFullDomain string = "full"
RuleTypeKeyword string = "keyword"
RuleTypeRegexp string = "regexp"
RuleTypeInclude string = "include"
)
type Entry struct {
Type string
Value string
@@ -64,32 +72,41 @@ func (l *ParsedList) toProto() (*router.GeoSite, error) {
}
for _, entry := range l.Entry {
switch entry.Type {
case "domain":
case RuleTypeDomain:
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_RootDomain,
Value: entry.Value,
Attribute: entry.Attrs,
})
case "regexp":
case RuleTypeRegexp:
// check regexp validity to avoid runtime error
_, err := regexp.Compile(entry.Value)
if err != nil {
return nil, fmt.Errorf("invalid regexp in list %s: %s", l.Name, entry.Value)
}
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_Regex,
Value: entry.Value,
Attribute: entry.Attrs,
})
case "keyword":
case RuleTypeKeyword:
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_Plain,
Value: entry.Value,
Attribute: entry.Attrs,
})
case "full":
case RuleTypeFullDomain:
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_Full,
Value: entry.Value,
Attribute: entry.Attrs,
})
default:
return nil, errors.New("unknown domain type: " + entry.Type)
return nil, fmt.Errorf("unknown domain type: %s", entry.Type)
}
}
return site, nil
@@ -99,7 +116,7 @@ func exportPlainTextList(list []string, refName string, pl *ParsedList) {
for _, listName := range list {
if strings.EqualFold(refName, listName) {
if err := pl.toPlainText(strings.ToLower(refName)); err != nil {
fmt.Println("Failed: ", err)
fmt.Println("Failed:", err)
continue
}
fmt.Printf("'%s' has been generated successfully.\n", listName)
@@ -118,24 +135,30 @@ func removeComment(line string) string {
func parseDomain(domain string, entry *Entry) error {
kv := strings.Split(domain, ":")
if len(kv) == 1 {
entry.Type = "domain"
entry.Type = RuleTypeDomain
entry.Value = strings.ToLower(kv[0])
return nil
}
if len(kv) == 2 {
entry.Type = strings.ToLower(kv[0])
entry.Value = strings.ToLower(kv[1])
if strings.EqualFold(entry.Type, RuleTypeRegexp) {
entry.Value = kv[1]
} else {
entry.Value = strings.ToLower(kv[1])
}
return nil
}
return errors.New("Invalid format: " + domain)
return fmt.Errorf("invalid format: %s", domain)
}
func parseAttribute(attr string) (*router.Domain_Attribute, error) {
var attribute router.Domain_Attribute
if len(attr) == 0 || attr[0] != '@' {
return &attribute, errors.New("invalid attribute: " + attr)
return &attribute, fmt.Errorf("invalid attribute: %s", attr)
}
// Trim attribute prefix `@` character
@@ -148,7 +171,7 @@ func parseAttribute(attr string) (*router.Domain_Attribute, error) {
attribute.Key = strings.ToLower(parts[0])
intv, err := strconv.Atoi(parts[1])
if err != nil {
return &attribute, errors.New("invalid attribute: " + attr + ": " + err.Error())
return &attribute, fmt.Errorf("invalid attribute: %s: %v", attr, err)
}
attribute.TypedValue = &router.Domain_Attribute_IntValue{IntValue: int64(intv)}
}
@@ -161,7 +184,7 @@ func parseEntry(line string) (Entry, error) {
var entry Entry
if len(parts) == 0 {
return entry, errors.New("empty entry")
return entry, fmt.Errorf("empty entry")
}
if err := parseDomain(parts[0], &entry); err != nil {
@@ -255,7 +278,7 @@ func ParseList(list *List, ref map[string]*List) (*ParsedList, error) {
newEntryList := make([]Entry, 0, len(entryList))
hasInclude := false
for _, entry := range entryList {
if entry.Type == "include" {
if entry.Type == RuleTypeInclude {
refName := strings.ToUpper(entry.Value)
if entry.Attrs != nil {
for _, attr := range entry.Attrs {
@@ -267,7 +290,7 @@ func ParseList(list *List, ref map[string]*List) (*ParsedList, error) {
refList := ref[refName]
if refList == nil {
return nil, errors.New(entry.Value + " not found.")
return nil, fmt.Errorf("list not found: %s", entry.Value)
}
attrEntrys := createIncludeAttrEntrys(refList, attr)
if len(attrEntrys) != 0 {
@@ -282,7 +305,7 @@ func ParseList(list *List, ref map[string]*List) (*ParsedList, error) {
pl.Inclusion[InclusionName] = true
refList := ref[refName]
if refList == nil {
return nil, errors.New(entry.Value + " not found.")
return nil, fmt.Errorf("list not found: %s", entry.Value)
}
newEntryList = append(newEntryList, refList.Entry...)
}
@@ -323,14 +346,14 @@ func main() {
return nil
})
if err != nil {
fmt.Println("Failed: ", err)
fmt.Println("Failed:", err)
os.Exit(1)
}
// Create output directory if not exist
if _, err := os.Stat(*outputDir); os.IsNotExist(err) {
if mkErr := os.MkdirAll(*outputDir, 0755); mkErr != nil {
fmt.Println("Failed: ", mkErr)
fmt.Println("Failed:", mkErr)
os.Exit(1)
}
}
@@ -340,12 +363,12 @@ func main() {
for refName, list := range ref {
pl, err := ParseList(list, ref)
if err != nil {
fmt.Println("Failed: ", err)
fmt.Println("Failed:", err)
os.Exit(1)
}
site, err := pl.toProto()
if err != nil {
fmt.Println("Failed: ", err)
fmt.Println("Failed:", err)
os.Exit(1)
}
protoList.Entry = append(protoList.Entry, site)
@@ -383,7 +406,7 @@ func main() {
os.Exit(1)
}
if err := os.WriteFile(filepath.Join(*outputDir, *outputName), protoBytes, 0644); err != nil {
fmt.Println("Failed: ", err)
fmt.Println("Failed:", err)
os.Exit(1)
} else {
fmt.Println(*outputName, "has been generated successfully.")