// Package holy implements main business logic of bot service. package holy import ( "context" "encoding/json" "errors" "fmt" "slices" "strings" "time" "github.com/carlmjohnson/requests" "golang.org/x/net/html" ) // APIClient клиент сервиса https://celebratoday.com/. func APIClient() (string, error) { var ( ctx = context.Background() url = "https://celebratoday.com/ru/today" respBody string ) // Формальная попытка притвориться браузером. _, _, day := time.Now().Date() userAgent := Config.UserAgents[day-1] ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() if err := requests.URL(url).UserAgent(userAgent). Header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"). Header("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"). Header("Accept-Charset", "utf-8").ToString(&respBody).Fetch(ctx); err != nil { return "", fmt.Errorf("Unable to GET %s: %w", url, err) //nolint: staticcheck // Looks like golangci-lint ignoring settings about fucking ST1005 check. We want readable errors despite what wants author of linter or assholes from go dev team. } holidays, err := parser(respBody) if err != nil { return "", fmt.Errorf("Unable to parse response from celebratoday.com: %w", err) //nolint: staticcheck // Looks like golangci-lint ignoring settings about fucking ST1005 check. We want readable errors despite what wants author of linter or assholes from go dev team. } if len(holidays) == 0 { return "", errors.New("Unable to parse response from celebratoday.com: no holidays found") //nolint: staticcheck // Looks like golangci-lint ignoring settings about fucking ST1005 check. We want readable errors despite what wants author of linter or assholes from go dev team. } slices.Sort(holidays) holidays = slices.Compact(holidays) return "* " + strings.Join(holidays, "\n* "), nil } // parser разбирает буфер с html-ем, выискивая праздники. func parser(data string) ([]string, error) { var ( holidays = make([]string, 0, 1) err error s = make([]Holy, 0, 1) ) doc, err := html.Parse(strings.NewReader(data)) if err != nil { return holidays, err } traverse(doc, &s) // А теперь пробежимся по полученной структуре и вытянем из неё все строки с праздниками. for _, ass := range s { for _, entity := range ass.MainEntity { holidays = append(holidays, entity.AcceptedAnswer.Text) for _, sugestedAnswer := range entity.SuggestedAnswer { holidays = append(holidays, sugestedAnswer.Text) } } } return holidays, err } // traverse рекурсивная функция, ищущая