package main import ( "encoding/json" "net/http" "net/url" "os" "strings" "time" tgbotapi "git.zio.sh/astra/telegram-bot-api" "github.com/astravexton/logger" "github.com/google/uuid" ) const openverseURL = "https://api.openverse.org/v1/images/" var httpClient = &http.Client{Timeout: 5 * time.Second} type openverseResponse struct { ResultCount int `json:"result_count"` Results []openverseImage `json:"results"` } type openverseImage struct { URL string `json:"url"` Thumbnail string `json:"thumbnail"` Title string `json:"title"` Width int `json:"width"` Height int `json:"height"` ForeignLandingURL string `json:"foreign_landing_url"` Source string `json:"source"` Filetype string `json:"filetype"` } type Results struct { Height int Image string Source string Thumbnail string Title string URL string Width int Filetype string } func main() { logger.Layout = time.Stamp logger.BitwiseLevel = logger.LogInfo | logger.LogWarning | logger.LogCritical bot, err := tgbotapi.NewBotAPI(os.Getenv("BOT_TOKEN")) if err != nil { logger.Critical(err.Error()) } me, err := bot.GetMe() if err != nil { logger.Critical(err.Error()) } logger.Info("Authorized as %s", me.UserName) u := tgbotapi.NewUpdate(0) u.Timeout = 60 updates := bot.GetUpdatesChan(u) for update := range updates { if update.InlineQuery != nil { if update.InlineQuery.Query != "" { logger.Debug("Inline query %q from %s", update.InlineQuery.Query, update.InlineQuery.From) var results []interface{} images := getImageList(update.InlineQuery.Query) for c, image := range images { if c == 50 { break } var result interface{} if image.Filetype == "gif" || strings.HasSuffix(strings.ToLower(image.Image), ".gif") { result = &tgbotapi.InlineQueryResultGIF{ Type: "gif", ID: uuid.New().String(), URL: image.Image, ThumbURL: image.Thumbnail, } } else { result = &tgbotapi.InlineQueryResultPhoto{ Type: "photo", ID: uuid.New().String(), URL: image.Image, ThumbURL: image.Thumbnail, } } results = append(results, result) } inlineConf := tgbotapi.InlineConfig{ InlineQueryID: update.InlineQuery.ID, IsPersonal: false, CacheTime: 300, Results: results, } bot.Request(inlineConf) } } } } func getImageList(query string) []Results { mature := "true" if strings.HasPrefix(query, "safe:on") { mature = "false" query = strings.TrimSpace(strings.TrimPrefix(query, "safe:on")) } if query == "" { return nil } // Anonymous requests are capped at 20; a registered token allows more. token := os.Getenv("OPENVERSE_TOKEN") pageSize := "20" if token != "" { pageSize = "50" } params := url.Values{} params.Set("q", query) params.Set("page_size", pageSize) params.Set("mature", mature) req, err := http.NewRequest(http.MethodGet, openverseURL+"?"+params.Encode(), nil) if err != nil { logger.Warning("building request: %s", err) return nil } req.Header.Set("User-Agent", "telegram-imagesearch-bot/1.0") if token != "" { req.Header.Set("Authorization", "Bearer "+token) } res, err := httpClient.Do(req) if err != nil { logger.Warning("openverse request failed: %s", err) return nil } defer res.Body.Close() if res.StatusCode != http.StatusOK { logger.Warning("openverse returned %s for %q", res.Status, query) return nil } var data openverseResponse if err := json.NewDecoder(res.Body).Decode(&data); err != nil { logger.Warning("decoding openverse response: %s", err) return nil } results := make([]Results, 0, len(data.Results)) for _, img := range data.Results { results = append(results, Results{ Height: img.Height, Image: img.URL, Source: img.Source, Thumbnail: img.Thumbnail, Title: img.Title, URL: img.ForeignLandingURL, Width: img.Width, Filetype: img.Filetype, }) } return results }