Initial commit
This commit is contained in:
110
internal/holy/celebratoday.go
Normal file
110
internal/holy/celebratoday.go
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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]
|
||||
|
||||
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", "en-US,en;q=0.5").
|
||||
Header("User-Agent", "ru-RU").
|
||||
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 {
|
||||
for _, sugestedAnswer := range entity.SuggestedAnswer {
|
||||
holidays = append(holidays, sugestedAnswer.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return holidays, err
|
||||
}
|
||||
|
||||
// traverse рекурсивная функция, ищущая <script type="application/ld+json"> в структуре документа. Там внутри будет json
|
||||
// со списком праздников и это и есть цель нашего поиска.
|
||||
func traverse(n *html.Node, h *[]Holy) {
|
||||
// Если найденный элемент в поле Data содержит script (то есть это тэг <script>).
|
||||
if n.Type == html.ElementNode && n.Data == "script" {
|
||||
// Ищем атрибут type со значением application/ld+json.
|
||||
for _, attr := range n.Attr {
|
||||
if attr.Key == "type" && attr.Val == "application/ld+json" {
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if c.Type == html.TextNode {
|
||||
// Добавлять рановато, надо вначале попробовать распарсить этот json
|
||||
// используя целевую структуру.
|
||||
var holiday Holy
|
||||
|
||||
if err := json.Unmarshal([]byte(c.Data), &holiday); err == nil {
|
||||
*h = append(*h, holiday)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
traverse(c, h)
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
Reference in New Issue
Block a user