diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..418f5c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +badger +db diff --git a/README.md b/README.md index a71f271..c41d410 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,8 @@ - TODO: add humblebundle - [msgbot](./cmd/msgbot/main.go) send messages which can be edited by other admins - needs env `disgo_token` - - TODO: implement \ No newline at end of file + - TODO: implement + +## TODO +- check Intents https://gist.github.com/advaith1/e69bcc1cdd6d0087322734451f15aa2f + - https://discordpy.readthedocs.io/en/stable/intents.html \ No newline at end of file diff --git a/cmd/dealsbot/api.go b/cmd/dealsbot/api.go new file mode 100644 index 0000000..f604689 --- /dev/null +++ b/cmd/dealsbot/api.go @@ -0,0 +1,14 @@ +package main + +type Api interface { + load() error + get() []Deal +} + +type DealsMap map[string]Deal + +type Deal struct { + Id string + Title string + Url string +} diff --git a/cmd/dealsbot/epic.go b/cmd/dealsbot/epic.go new file mode 100644 index 0000000..039486d --- /dev/null +++ b/cmd/dealsbot/epic.go @@ -0,0 +1,129 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/disgoorg/log" + "io" + "net/http" + "time" +) + +type EpicStruct struct { + url string + idPrefix string + deals DealsMap +} + +func newEpicApi() EpicStruct { + return EpicStruct{ + url: "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions", + idPrefix: "epic-", + deals: make(map[string]Deal), + } +} + +type epicApiBody struct { + Data struct { + Catalog struct { + SearchStore struct { + Elements []struct { + Title string `json:"title"` + Id string `json:"id"` + Description string `json:"description"` + OfferType string `json:"offerType"` + IsCodeRedemptionOnly bool `json:"isCodeRedemptionOnly"` + ProductSlug string `json:"productSlug"` + OfferMappings []struct { + PageSlug string `json:"pageSlug"` + PageType string `json:"pageType"` + } `json:"offerMappings"` + Price struct { + TotalPrice struct { + DiscountPrice int `json:"discountPrice"` + OriginalPrice int `json:"originalPrice"` + Discount int `json:"discount"` + CurrencyCode string `json:"currencyCode"` + } `json:"totalPrice"` + } `json:"price"` + Promotions *struct { + PromotionalOffers []struct { + PromotionalOffers []struct { + StartDate time.Time `json:"startDate"` + EndDate time.Time `json:"endDate"` + DiscountSetting struct { + DiscountType string `json:"discountType"` + DiscountPercentage int `json:"discountPercentage"` + } `json:"discountSetting"` + } `json:"promotionalOffers"` + } `json:"promotionalOffers"` + } `json:"promotions"` + } `json:"elements"` + Paging struct { + Count int `json:"count"` + Total int `json:"total"` + } `json:"paging"` + } `json:"searchStore"` + } `json:"Catalog"` + } `json:"data"` +} + +func (e EpicStruct) load() error { + client := &http.Client{} + req, err := http.NewRequest("GET", e.url, nil) + if err != nil { + return err + } + + res, err := client.Do(req) + if err != nil { + return err + } + body, err := io.ReadAll(res.Body) + if err != nil { + return err + } + var data epicApiBody + err = json.Unmarshal(body, &data) + if err != nil { + return err + } + + for _, element := range data.Data.Catalog.SearchStore.Elements { + if element.Promotions == nil || + len(element.Promotions.PromotionalOffers) == 0 || + len(element.Promotions.PromotionalOffers[0].PromotionalOffers) == 0 || + element.Promotions.PromotionalOffers[0].PromotionalOffers[0].DiscountSetting.DiscountPercentage != 0 { + // no deal + continue + } + productSlug := element.ProductSlug + if len(element.OfferMappings) != 0 && productSlug == "" { + productSlug = element.OfferMappings[0].PageSlug + } + if productSlug == "" { + log.Error(fmt.Sprintf("product slug not found for: %v", element.Title)) + continue + } + + id := fmt.Sprintf("%v%v", e.idPrefix, element.Id) + title := element.Title + url := fmt.Sprintf("https://store.epicgames.com/en-US/p/%v", productSlug) + + e.deals[id] = Deal{ + Id: id, + Title: title, + Url: url, + } + } + + return nil +} + +func (e EpicStruct) get() []Deal { + var deals []Deal + for _, deal := range e.deals { + deals = append(deals, deal) + } + return deals +} diff --git a/cmd/dealsbot/gog.go b/cmd/dealsbot/gog.go new file mode 100644 index 0000000..5fc4dd6 --- /dev/null +++ b/cmd/dealsbot/gog.go @@ -0,0 +1,135 @@ +package main + +import ( + "fmt" + "golang.org/x/net/html" + "net/http" + "regexp" +) + +type GogStruct struct { + url string + baseUrl string + idPrefix string + deals DealsMap +} + +func newGogApi() GogStruct { + return GogStruct{ + url: "https://www.gog.com/en", + baseUrl: "https://www.gog.com/en/game/", + idPrefix: "gog-", + deals: make(map[string]Deal), + } +} + +func (e GogStruct) load() error { + client := &http.Client{} + // might have to add a cookie at a later time but currently works without + // "Cookie", "gog_lc=GB_GBP_en-US" or "Accept-Language", "en" + reqStore, err := http.NewRequest("GET", e.url, nil) + if err != nil { + return err + } + + resStore, err := client.Do(reqStore) + if err != nil { + return err + } + bodyStore := html.NewTokenizer(resStore.Body) + + regexAppid, err := regexp.Compile(`/en/game/([-\w]+)`) + if err != nil { + return err + } + + var appIDs []string + func() { + for { + tt := bodyStore.Next() + + switch { + case tt == html.ErrorToken: + // file end or error + return + case tt == html.StartTagToken: + t := bodyStore.Token() + if t.Data != "a" { + continue + } + for _, a := range t.Attr { + if !(a.Key == "id" && a.Val == "giveaway") { + continue + } + for _, attr := range t.Attr { + if attr.Key != "ng-href" { + continue + } + appID := regexAppid.FindStringSubmatch(attr.Val) + if len(appID) < 1 { + continue + } + appIDs = append(appIDs, appID[1]) + } + } + } + } + }() + + for _, appID := range appIDs { + reqGame, err := http.NewRequest("GET", fmt.Sprintf("%v%v", e.baseUrl, appID), nil) + if err != nil { + return err + } + + resGame, err := client.Do(reqGame) + if err != nil { + return err + } + bodyGame := html.NewTokenizer(resGame.Body) + + func() { + for { + tt := bodyGame.Next() + + switch { + case tt == html.ErrorToken: + // file end or error + return + case tt == html.StartTagToken: + t := bodyGame.Token() + if t.Data != "h1" { + continue + } + for _, a := range t.Attr { + if !(a.Key == "class" && a.Val == "productcard-basics__title") { + + } + if tt = bodyGame.Next(); tt != html.TextToken { + continue + } + id := fmt.Sprintf("%v%v", e.idPrefix, appID) + title := bodyGame.Token().Data + url := fmt.Sprintf("%v%v", e.baseUrl, appID) + + e.deals[id] = Deal{ + Id: id, + Title: title, + Url: url, + } + } + } + } + }() + } + + return nil +} + +func (e GogStruct) get() []Deal { + var deals []Deal + for _, deal := range e.deals { + deals = append(deals, deal) + } + return deals +} diff --git a/cmd/dealsbot/humblebundle.go b/cmd/dealsbot/humblebundle.go new file mode 100644 index 0000000..75e0372 --- /dev/null +++ b/cmd/dealsbot/humblebundle.go @@ -0,0 +1,137 @@ +package main + +import ( + "encoding/json" + "fmt" + "golang.org/x/net/html" + "net/http" + "regexp" +) + +type HumbleBundleStruct struct { + url string + baseUrl string + idPrefix string + deals DealsMap +} + +func newHumbleBundleApi() HumbleBundleStruct { + return HumbleBundleStruct{ + url: "https://www.humblebundle.com/", + baseUrl: "https://www.humblebundle.com/store/", + idPrefix: "humblebundle-", + deals: make(map[string]Deal), + } +} + +type humblebundleJsonBody struct { + Mosaic []struct { + Products []struct { + ProductUrl string `json:"product_url,omitempty"` + Highlights []string `json:"highlights,omitempty"` + TileName string `json:"tile_name,omitempty"` + ProductTitle *string `json:"product_title,omitempty"` + Type string `json:"type"` + Category string `json:"category,omitempty"` + EndDateDatetime string `json:"end_date|datetime,omitempty"` + OperatingSystems []string `json:"operating_systems,omitempty"` + Platforms []string `json:"platforms,omitempty"` + } `json:"products"` + } `json:"mosaic"` +} + +func (e HumbleBundleStruct) load() error { + client := &http.Client{} + // might have to add a cookie at a later time but currently works without + // "Cookie", "gog_lc=GB_GBP_en-US" or "Accept-Language", "en" + reqStore, err := http.NewRequest("GET", e.url, nil) + if err != nil { + return err + } + + resStore, err := client.Do(reqStore) + if err != nil { + return err + } + bodyStore := html.NewTokenizer(resStore.Body) + + var data humblebundleJsonBody + err = func() error { + for { + tt := bodyStore.Next() + + switch { + case tt == html.ErrorToken: + // file end or error + return nil + case tt == html.StartTagToken: + t := bodyStore.Token() + if t.Data != "script" { + continue + } + for _, a := range t.Attr { + if !(a.Key == "id" && a.Val == "webpack-json-data") { + continue + } + if tt = bodyStore.Next(); tt != html.TextToken { + continue + } + err = json.Unmarshal([]byte(bodyStore.Token().Data), &data) + if err != nil { + return err + } + } + } + } + }() + if err != nil { + return err + } + + regexAppid, err := regexp.Compile(`/store/([-\w]+)`) + if err != nil { + return err + } + + for _, products := range data.Mosaic { + for _, product := range products.Products { + if !contains(product.Highlights, "FREE WHILE SUPPLIES LAST") { + continue + } + + appID := regexAppid.FindStringSubmatch(product.ProductUrl) + if len(appID) < 1 { + continue + } + id := fmt.Sprintf("%v%v", e.idPrefix, appID[1]) + title := product.TileName + url := fmt.Sprintf("%v%v", e.baseUrl, appID[1]) + + e.deals[id] = Deal{ + Id: id, + Title: title, + Url: url, + } + } + } + + return nil +} + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + +func (e HumbleBundleStruct) get() []Deal { + var deals []Deal + for _, deal := range e.deals { + deals = append(deals, deal) + } + return deals +} diff --git a/cmd/dealsbot/main.go b/cmd/dealsbot/main.go index a77585e..530813b 100644 --- a/cmd/dealsbot/main.go +++ b/cmd/dealsbot/main.go @@ -1,9 +1,121 @@ package main +import ( + "context" + "errors" + "fmt" + "github.com/dgraph-io/badger/v4" + "github.com/disgoorg/disgo" + "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/disgo/rest" + "github.com/disgoorg/disgo/webhook" + "github.com/disgoorg/log" + "github.com/disgoorg/snowflake/v2" + "os" + "os/signal" + "reflect" + "syscall" + "time" +) + +var ( + webhookID = snowflake.GetEnv("webhook_id") + webhookToken = os.Getenv("webhook_token") +) + func main() { // translate this to go: https://dev.rievo.net/sst/feed-python // query different sources store to db - // try to incorporate operagx api + // try to incorporate operagx apiUrl: + // - https://gx-proxy.operacdn.com/content/free-games?_limit=300&_sort=order%3AASC // send messages to discord + // ideas: + // - https://github.com/TheLovinator1/discord-free-game-notifier + // - https://gg.deals/games/free-games/ + // - https://gg.deals/news/free-gog-games/ + // - origin + // - check ubisoft works + + log.SetLevel(log.LevelDebug) + log.Info("starting dealsbot...") + log.Info("disgo version: ", disgo.Version) + + client := webhook.New(webhookID, webhookToken) + defer client.Close(context.TODO()) + + repo := InitDb() + defer repo.Close() + + ticker := time.NewTicker(10 * time.Minute) + tickerGC := time.NewTicker(15 * time.Minute) + quit := make(chan struct{}) + + go func() { + for { + select { + case <-ticker.C: + var apis []Api + apis = append(apis, newUbsioftApi(), newEpicApi(), newSteamApi(), newGogApi(), newHumbleBundleApi()) + for _, api := range apis { + err := api.load() + if err != nil { + log.Error(err) + } + } + var deals []Deal + for _, api := range apis { + apiDeals := api.get() + deals = append(deals, apiDeals...) + } + + for _, deal := range deals { + retrievedDeal, _ := repo.GetValue(deal.Id) + + if deal.Id == retrievedDeal.Id { + log.Debugf("%v is already published", deal.Id) + } else if reflect.DeepEqual(deal, retrievedDeal) { + log.Errorf("%v is published but not equal", deal.Id) + } else { + log.Infof("%v is new and will be published", deal.Id) + go sendWebhook(client, deal) + err := repo.SetValue(deal) + if err != nil { + log.Error(err) + } + } + } + + case <-tickerGC.C: + err := repo.RunGC() + if err != nil && !errors.Is(err, badger.ErrNoRewrite) { + log.Errorf("error with GC: %v", err) + } else { + log.Debug("GC successful") + } + case <-quit: + ticker.Stop() + tickerGC.Stop() + return + } + } + }() + + log.Infof("dealsbot is now running. Press CTRL-C to exit.") + s := make(chan os.Signal, 1) + signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) + <-s +} + +func sendWebhook(client webhook.Client, deal Deal) { + var status string + + status = fmt.Sprintf("currently free: %v\n", deal.Url) + + if _, err := client.CreateMessage(discord.NewWebhookMessageCreateBuilder(). + SetContent(status).Build(), + rest.WithDelay(2*time.Second), + ); err != nil { + log.Errorf("error sending message %v", err.Error()) + } } diff --git a/cmd/dealsbot/repository.go b/cmd/dealsbot/repository.go new file mode 100644 index 0000000..f6e15b1 --- /dev/null +++ b/cmd/dealsbot/repository.go @@ -0,0 +1,121 @@ +package main + +import ( + "encoding/json" + "github.com/dgraph-io/badger/v4" + "github.com/disgoorg/log" +) + +type Repository interface { + GetAll() ([]Deal, error) + GetValue(dealId string) Deal + SetValue(deal Deal) error + DeleteValue(dealId string) error + Close() error +} + +type DealRepository struct { + db *badger.DB +} + +func InitDb() *DealRepository { + opts := badger.DefaultOptions("./db") + opts.Logger = nil + db, err := badger.Open(opts) + if err != nil { + log.Fatal(err) + } + return &DealRepository{db} +} + +func (d *DealRepository) Close() error { + return d.db.Close() +} + +func (d *DealRepository) RunGC() error { + return d.db.RunValueLogGC(0.7) +} + +func (d *DealRepository) GetAll() ([]Deal, error) { + var deals []Deal + err := d.db.View(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + opts.PrefetchSize = 10 + it := txn.NewIterator(opts) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + item := it.Item() + err := item.Value(func(val []byte) error { + retrievedDeal := Deal{} + err := json.Unmarshal(val, &retrievedDeal) + deals = append(deals, retrievedDeal) + return err + }) + if err != nil { + return err + } + } + return nil + }) + return deals, err +} + +func (d *DealRepository) GetValue(dealId string) (Deal, error) { + retrievedDeal := Deal{} + err := d.db.View(func(txn *badger.Txn) error { + item, err := txn.Get([]byte(dealId)) + if err != nil { + return err + } + err = item.Value(func(val []byte) error { + err = json.Unmarshal(val, &retrievedDeal) + return err + }) + return err + }) + if err != nil { + return Deal{}, err + } + return retrievedDeal, nil +} + +func (d *DealRepository) SetValue(deal Deal) error { + jsonBytes, err := json.Marshal(deal) + if err != nil { + return err + } + err = d.db.Update(func(txn *badger.Txn) error { + err := txn.Set([]byte(deal.Id), jsonBytes) + return err + }) + if err != nil { + return err + } + return nil +} + +func (d *DealRepository) DeleteValue(dealId string) error { + err := d.db.Update(func(txn *badger.Txn) error { + err := txn.Delete([]byte(dealId)) + return err + }) + return err +} + +func (d *DealRepository) DeleteAll() error { + err := d.db.Update(func(txn *badger.Txn) error { + opts := badger.DefaultIteratorOptions + opts.PrefetchSize = 10 + it := txn.NewIterator(opts) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + item := it.Item() + err := txn.Delete(item.Key()) + if err != nil { + return err + } + } + return nil + }) + return err +} diff --git a/cmd/dealsbot/steam.go b/cmd/dealsbot/steam.go new file mode 100644 index 0000000..f6916e0 --- /dev/null +++ b/cmd/dealsbot/steam.go @@ -0,0 +1,146 @@ +package main + +import ( + "encoding/json" + "fmt" + "golang.org/x/net/html" + "io" + "net/http" + "regexp" +) + +type SteamStruct struct { + url string + baseUrl string + apiUrl string + idPrefix string + deals DealsMap +} + +func newSteamApi() SteamStruct { + return SteamStruct{ + url: "https://store.steampowered.com/search/results?force_infinite=1&maxprice=free&specials=1", + baseUrl: "https://store.steampowered.com/app/", + apiUrl: "https://store.steampowered.com/api/appdetails?appids=", + idPrefix: "steam-", + deals: make(map[string]Deal), + } +} + +type steamApiBodyGame struct { + Success bool `json:"success"` + Data struct { + Type string `json:"type"` + Name string `json:"name"` + IsFree bool `json:"is_free"` + PriceOverview struct { + Currency string `json:"currency"` + Initial int `json:"initial"` + Final int `json:"final"` + DiscountPercent int `json:"discount_percent"` + InitialFormatted string `json:"initial_formatted"` + FinalFormatted string `json:"final_formatted"` + } `json:"price_overview"` + Platforms struct { + Windows bool `json:"windows"` + Mac bool `json:"mac"` + Linux bool `json:"linux"` + } `json:"platforms"` + } `json:"data"` +} + +type steamApiBody map[string]steamApiBodyGame + +func (e SteamStruct) load() error { + client := &http.Client{} + reqStore, err := http.NewRequest("GET", e.url, nil) + if err != nil { + return err + } + + resStore, err := client.Do(reqStore) + if err != nil { + return err + } + bodyStore := html.NewTokenizer(resStore.Body) + + // could also search over each #search_resultsRows element instead of regex + regexAppid, err := regexp.Compile(`https://store\.steampowered\.com/app/(\d+)`) + if err != nil { + return err + } + + var appIDs []string + func() { + + for { + tt := bodyStore.Next() + + switch { + case tt == html.ErrorToken: + // file end or error + return + case tt == html.StartTagToken: + t := bodyStore.Token() + if t.Data != "a" { + continue + } + for _, a := range t.Attr { + if a.Key != "href" { + continue + } + appID := regexAppid.FindStringSubmatch(a.Val) + if len(appID) < 1 { + continue + } + appIDs = append(appIDs, appID[1]) + } + } + } + }() + + for _, appID := range appIDs { + reqApi, err := http.NewRequest("GET", fmt.Sprintf("%v%v", e.apiUrl, appID), nil) + if err != nil { + return err + } + + resApi, err := client.Do(reqApi) + if err != nil { + return err + } + bodyApi, err := io.ReadAll(resApi.Body) + if err != nil { + return err + } + var data steamApiBody + err = json.Unmarshal(bodyApi, &data) + if err != nil { + return err + } + if game, ok := data[appID]; ok { + if game.Data.Type != "game" { + continue + } + id := fmt.Sprintf("%v%v", e.idPrefix, appID) + title := game.Data.Name + url := fmt.Sprintf("%v%v", e.baseUrl, appID) + + e.deals[id] = Deal{ + Id: id, + Title: title, + Url: url, + } + } + } + + return nil +} + +func (e SteamStruct) get() []Deal { + var deals []Deal + for _, deal := range e.deals { + deals = append(deals, deal) + } + return deals +} diff --git a/cmd/dealsbot/ubisoft.go b/cmd/dealsbot/ubisoft.go new file mode 100644 index 0000000..63d546e --- /dev/null +++ b/cmd/dealsbot/ubisoft.go @@ -0,0 +1,143 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "regexp" +) + +type UbisoftStruct struct { + url string + idPrefix string + headers map[string]string + deals DealsMap +} + +func newUbsioftApi() UbisoftStruct { + ubisoft := UbisoftStruct{ + url: "https://free.ubisoft.com/configuration.js", + idPrefix: "ubisoft-", + headers: make(map[string]string), + deals: make(map[string]Deal), + } + ubisoft.headers["referer"] = "https://free.ubisoft.com/" + ubisoft.headers["origin"] = "https://free.ubisoft.com" + ubisoft.headers["ubi-localecode"] = "en-US" + ubisoft.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0" + + return ubisoft +} + +type ubisoftApiBody struct { + News []struct { + Type string `json:"type"` + Title string `json:"title"` + Links []struct { + Param string `json:"param"` + } `json:"links"` + } `json:"news"` +} + +func (e UbisoftStruct) load() error { + + appId, prodUrl, err := func() (string, string, error) { + client := &http.Client{} + req, err := http.NewRequest("GET", e.url, nil) + if err != nil { + return "", "", err + } + + for key, value := range e.headers { + req.Header.Set(key, value) + } + res, err := client.Do(req) + if err != nil { + return "", "", err + } + body, err := io.ReadAll(res.Body) + regexAppId, err := regexp.Compile(`appId:\s*'(.+)'`) + if err != nil { + return "", "", err + } + regexProd, err := regexp.Compile(`prod:\s*'(.+)'`) + if err != nil { + return "", "", err + } + appId := regexAppId.FindSubmatch(body) + prodUrl := regexProd.FindSubmatch(body) + + if len(appId) < 1 || len(prodUrl) < 1 { + return "", "", errors.New("appid or prod url not found") + } + + return string(appId[1]), string(prodUrl[1]), nil + }() + + if err != nil { + return err + } + + client := &http.Client{} + req, err := http.NewRequest("GET", prodUrl, nil) + if err != nil { + return err + } + + for key, value := range e.headers { + req.Header.Set(key, value) + } + req.Header.Set("ubi-appid", appId) + res, err := client.Do(req) + if err != nil { + return err + } + body, err := io.ReadAll(res.Body) + + var data ubisoftApiBody + err = json.Unmarshal(body, &data) + if err != nil { + return err + } + + for _, news := range data.News { + if news.Type != "freegame" { + continue + } + + title := news.Title + if len(news.Links) != 1 { + return errors.New(fmt.Sprintf("lenght of links for %v is more or less then 1", news.Title)) + } + + regexName, err := regexp.Compile(`https://register.ubisoft.com/([^/]*)/?`) + idFromUrl := regexName.FindStringSubmatch(news.Links[0].Param) + if err != nil { + return err + } + if len(idFromUrl) < 1 { + return errors.New("could not parse url") + } + + id := fmt.Sprintf("%v%v", e.idPrefix, idFromUrl[1]) + url := news.Links[0].Param + + e.deals[id] = Deal{ + Id: id, + Title: title, + Url: url, + } + } + + return nil +} + +func (e UbisoftStruct) get() []Deal { + var deals []Deal + for _, deal := range e.deals { + deals = append(deals, deal) + } + return deals +} diff --git a/cmd/groupbot/main.go b/cmd/groupbot/main.go new file mode 100644 index 0000000..81956a6 --- /dev/null +++ b/cmd/groupbot/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "context" + "fmt" + "github.com/disgoorg/disgo" + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/cache" + "github.com/disgoorg/disgo/gateway" + "github.com/disgoorg/log" + "github.com/disgoorg/snowflake/v2" + "os" +) + +var ( + token = os.Getenv("disgo_token") + registerGuildID = snowflake.GetEnv("disgo_guild_id") +) + +func main() { + + log.SetLevel(log.LevelDebug) + log.Info("starting groupbot...") + log.Info("disgo version: ", disgo.Version) + + // permissions: + // intent: + client, err := disgo.New(token, + bot.WithGatewayConfigOpts( + gateway.WithIntents(gateway.IntentsNone), + ), + bot.WithCacheConfigOpts( + cache.WithCaches( + cache.FlagsNone, + ), + ), + ) + if err != nil { + log.Fatal("error while building disgo instance: ", err) + return + } + defer client.Close(context.TODO()) + + var groups Groups + for i := 0; i < 30; i++ { + groups = append(groups, Group{ + name: fmt.Sprintf("g%v", i), + group: "", + emoji: "", + }) + } + createGroupMessage(groups) + + if err = client.OpenGateway(context.TODO()); err != nil { + log.Fatal("error while connecting to gateway: ", err) + } + + log.Infof("groupbot is now running. Press CTRL-C to exit.") + //s := make(chan os.Signal, 1) + //signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) + //<-s +} + +type Group struct { + name string + group string + emoji string +} + +type Groups []Group + +// chunk +// source https://github.com/golang/go/issues/53987 +func chunk[T any](arrIn []T, size int) (arrOut [][]T) { + for i := 0; i < len(arrIn); i += size { + end := i + size + if end > len(arrIn) { + end = len(arrIn) + } + arrOut = append(arrOut, arrIn[i:end]) + } + return arrOut +} + +func createGroupMessage(groups Groups) { + groupsMessages := chunk(chunk(groups, 5), 5) + + for _, messageGroups := range groupsMessages { + fmt.Println("--- new message ---") + for _, rowGroups := range messageGroups { + fmt.Printf("new row: ") + for _, group := range rowGroups { + fmt.Printf("%v;", group.name) + } + fmt.Println("") + } + } +} diff --git a/cmd/tempbot/main.go b/cmd/tempbot/main.go index d18b639..0c9f88c 100644 --- a/cmd/tempbot/main.go +++ b/cmd/tempbot/main.go @@ -21,7 +21,7 @@ var ( ) func main() { - log.SetLevel(log.LevelInfo) + log.SetLevel(log.LevelDebug) log.Info("starting tempbot...") log.Info("disgo version: ", disgo.Version) @@ -61,13 +61,15 @@ func main() { // delete messages older then x min in channel - ticker := time.NewTicker(5 * time.Minute) + ticker := time.NewTicker(1 * time.Minute) quit := make(chan struct{}) go func() { + client.Logger().Debug("does it even run") for { select { case <-ticker.C: messages, err := client.Rest().GetMessages(channelTempID, 0, 0, 0, 100) + client.Logger().Info(len(messages)) if err != nil { client.Logger().Error("error getting messages: ", err) } diff --git a/go.mod b/go.mod index 67bce6d..4460b0b 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,35 @@ module grow.rievo.dev/discordBots go 1.20 require ( - github.com/disgoorg/disgo v0.15.0 - github.com/disgoorg/log v1.2.0 + github.com/dgraph-io/badger/v4 v4.0.1 + github.com/disgoorg/disgo v0.15.1-0.20230203231532-07092d420798 + github.com/disgoorg/log v1.2.1-0.20220709130509-d138f028c3b7 github.com/disgoorg/snowflake/v2 v2.0.1 + github.com/k0kubun/pp/v3 v3.2.0 + golang.org/x/net v0.5.0 ) require ( + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/disgoorg/json v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/protobuf v1.3.1 // indirect + github.com/golang/snappy v0.0.3 // indirect + github.com/google/flatbuffers v1.12.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/klauspost/compress v1.12.3 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b // indirect + go.opencensus.io v0.22.5 // indirect golang.org/x/crypto v0.5.0 // indirect - golang.org/x/exp v0.0.0-20230124195608-d38c7dcee874 // indirect + golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9 // indirect golang.org/x/sys v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index 81c45b4..8da68f3 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,136 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/disgoorg/disgo v0.15.0 h1:OTYlhyQ/frPtqY9dGRSaBpo7GJtTQj+8dah2ml+LPHM= -github.com/disgoorg/disgo v0.15.0/go.mod h1:j7MCI6foUipYNozxwttr2hcaEQa3gNEbcwgLnyLUf6E= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v4 v4.0.1 h1:zwLYFc4sfxKdaRTvS6wlHsSuYWNUiWnYLU+TS+/nCDI= +github.com/dgraph-io/badger/v4 v4.0.1/go.mod h1:edFJfgVfwYjg+grodpS7Yj2vohQMK3VL6eCaR6EpRJU= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/disgoorg/disgo v0.15.1-0.20230203231532-07092d420798 h1:BW5wOwP53TX8XgjrSSL6DiIzKijHpKBDl/jWAGYGT20= +github.com/disgoorg/disgo v0.15.1-0.20230203231532-07092d420798/go.mod h1:j7MCI6foUipYNozxwttr2hcaEQa3gNEbcwgLnyLUf6E= github.com/disgoorg/json v1.0.0 h1:kDhSM661fgIuNoZF3BO5/odaR5NSq80AWb937DH+Pdo= github.com/disgoorg/json v1.0.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA= -github.com/disgoorg/log v1.2.0 h1:sqlXnu/ZKAlIlHV9IO+dbMto7/hCQ474vlIdMWk8QKo= -github.com/disgoorg/log v1.2.0/go.mod h1:3x1KDG6DI1CE2pDwi3qlwT3wlXpeHW/5rVay+1qDqOo= +github.com/disgoorg/log v1.2.1-0.20220709130509-d138f028c3b7 h1:TDiZG14dzJgtdlt4sZjgLvtpA3Q0yxK9QMo1R1p2LCs= +github.com/disgoorg/log v1.2.1-0.20220709130509-d138f028c3b7/go.mod h1:hhQWYTFTnIGzAuFPZyXJEi11IBm9wq+/TVZt/FEwX0o= github.com/disgoorg/snowflake/v2 v2.0.1 h1:CuUxGLwggUxEswZOmZ+mZ5i0xSumQdXW9tXW7uGqe+0= github.com/disgoorg/snowflake/v2 v2.0.1/go.mod h1:SPU9c2CNn5DSyb86QcKtdZgix9osEtKrHLW4rMhfLCs= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b h1:qYTY2tN72LhgDj2rtWG+LI6TXFl2ygFQQ4YezfVaGQE= github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/exp v0.0.0-20230124195608-d38c7dcee874 h1:kWC3b7j6Fu09SnEBr7P4PuQyM0R6sqyH9R+EjIvT1nQ= -golang.org/x/exp v0.0.0-20230124195608-d38c7dcee874/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9 h1:frX3nT9RkKybPnjyI+yvZh6ZucTZatCCEm9D47sZ2zo= +golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=