godette/docs.go

123 lines
3.5 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"strings"
"time"
"github.com/go-telegram/bot"
)
var docApiURL = "https://docs.godotengine.org/_/api/v3/search/?q=project:godot %s"
var docURL = "https://docs.godotengine.org/en/stable/search.html?q=%s"
var docApiURL_ru = "https://docs.godotengine.org/_/api/v3/search/?q=project:godot-ru %s"
var docURL_ru = "https://docs.godotengine.org/ru/4.x/search.html?q=%s"
var lastDocSearch = time.Now().Add(-1 * time.Hour)
type DocResponse struct {
Count uint `json:"count"`
Next string `json:"next"`
Previous string `json:"previous"`
Results []DocResult `json:"results"`
}
type DocResult struct {
Title string `json:"title"`
Domain string `json:"domain"`
Path string `json:"path"`
Highlights DocHighlights `json:"highlights"`
}
type DocHighlights struct {
Title []string `json:"title"`
}
func getAndDecode[T any](u string) (*T, error) {
req, err := url.ParseRequestURI(u)
if err != nil {
log.Println("Can't parse url: ", err)
return nil, err
}
result, err := http.Get(req.String())
if err != nil {
log.Println("Can't get http: ", err)
return nil, err
}
defer result.Body.Close()
var response T
err = json.NewDecoder(result.Body).Decode(&response)
if err != nil {
return nil, err
}
return &response, nil
}
func getDocs(topic string, russian bool) (string, error) {
apiURL := docApiURL
pageURL := docURL
if russian {
apiURL = docApiURL_ru
pageURL = docURL_ru
}
since := time.Since(lastDocSearch)
GO_SEARCH_YOURSELF := fmt.Sprintf("\n\n[Попробуйте поискать самостоятельно\\!](%s)", fmt.Sprintf(pageURL, url.QueryEscape(topic)))
if since < 5*time.Second {
return "Извините, запросы происходят слишком часто" + GO_SEARCH_YOURSELF, nil
}
lastDocSearch = time.Now()
topic_escaped := bot.EscapeMarkdown(topic)
not_found := fmt.Sprintf("Извините, по запросу *%s* ничего не найдено\\."+GO_SEARCH_YOURSELF, topic_escaped)
exactUrl := fmt.Sprintf(apiURL, url.QueryEscape(topic))
looselyUrl := fmt.Sprintf(apiURL, url.QueryEscape(topic+"*"))
response, err := getAndDecode[DocResponse](exactUrl)
if err != nil || len(response.Results) == 0 {
response, err = getAndDecode[DocResponse](looselyUrl)
if err != nil || len(response.Results) == 0 {
return not_found, nil
}
}
textResults := ""
for i, r := range response.Results {
if i > 9 {
break
}
text := bot.EscapeMarkdown(r.Title)
link, _ := url.JoinPath(r.Domain, r.Path)
textResults += fmt.Sprintf("%d\\. [%s](%s)\n", i+1, text, link)
}
textResults += fmt.Sprintf("\n\n Или вы можете попробовать [поискать самостоятельно](%s)\\!", fmt.Sprintf(pageURL, topic))
text := fmt.Sprintf("Вот что я нашла по запросу *%s*: \n\n%s", topic_escaped, textResults)
return text, nil
}
func HandleDocRequest(g Godette) {
topic := strings.TrimPrefix(g.update.Message.Text, "/docs ")
text, docerr := getDocs(topic, false)
if docerr != nil {
log.Println("Can't get docs: ", docerr)
}
_, err := g.ReplyWithMarkdown(text)
if err != nil {
log.Println(err)
}
}
func HandleDocRequestRu(g Godette) {
topic := strings.TrimPrefix(g.update.Message.Text, "/docs_ru ")
text, docerr := getDocs(topic, true)
if docerr != nil {
log.Println("Can't get docs: ", docerr)
}
_, err := g.ReplyWithMarkdown(text)
if err != nil {
log.Println(err)
}
}