package main import ( "crypto/tls" "fmt" "log" "net/http" "os" "strconv" "strings" "unicode" "git.fromouter.space/hamcha/tg" "github.com/spf13/viper" ) type config struct { Token string Bind string WebhookURL string WebhookPath string MaxRequestsPerMessage int Log bool } func checkErr(err error, msg string, args ...interface{}) { if err != nil { fmt.Printf("FATAL ERROR\n"+msg+":\n ", args...) fmt.Println(err.Error()) os.Exit(1) } } var api *tg.Telegram var cfg config func main() { viper.AutomaticEnv() bind := viper.GetString("bind") if bind == "" { panic("Missing BIND") } token := viper.GetString("token") if token == "" { panic("Missing TOKEN") } webhookURL := viper.GetString("webhookurl") if webhookURL == "" { panic("Missing WEBHOOKURL") } webhookPath := viper.GetString("webhookpath") if webhookURL == "" { panic("Missing WEBHOOKPATH") } // Set default maxreq if cfg.MaxRequestsPerMessage < 1 { cfg.MaxRequestsPerMessage = 5 } // Ignore CA errors http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} api = tg.MakeAPIClient(token) api.SetWebhook(webhookURL) api.HandleWebhook(bind, webhookPath, webhook) } type CardFaceFlipPics struct { Normal string Flipped string Ponyhead string MCM string } func webhook(update tg.APIUpdate) { // Handle inline queries (99% of the usage I hope) if update.Inline != nil && update.Inline.Query != "" { query := update.Inline.Query results, err := mlpapiSearch(query) if err != nil { fmt.Println(err) // DO SOMETHING return } if cfg.Log { log.Printf(" -> %d cards", len(results.Data)) } photos := make([]tg.APIInlineQueryResultPhoto, len(results.Data)) for i, card := range results.Data { face, caption, buttons := getCardEntry(card, 0) photos[i] = tg.APIInlineQueryResultPhoto{ Type: "photo", ResultID: strconv.FormatInt(card.GUID, 10), PhotoURL: face, ThumbURL: face, Title: card.FullName, Caption: caption, Width: 344, Height: 480, ReplyMarkup: &tg.APIInlineKeyboardMarkup{ InlineKeyboard: [][]tg.APIInlineKeyboardButton{buttons}, }, } // Flip width/height for problems if card.Type == "Problem" { photos[i].Width, photos[i].Height = photos[i].Height, photos[i].Width } } err = api.AnswerInlineQuery(tg.InlineQueryResponse{ QueryID: update.Inline.QueryID, Results: photos, }) if err != nil { fmt.Println(err) // DO SOMETHING return } } // Check for card requests if update.Message != nil && update.Message.Text != nil { requests := getCardRequests(*update.Message.Text) if len(requests) > cfg.MaxRequestsPerMessage { api.SendTextMessage(tg.ClientTextMessageData{ ChatID: update.Message.Chat.ChatID, Text: fmt.Sprintf("You asked for way too many cards (%d!), please only ask me for at most %d cards in a single message.", len(requests), cfg.MaxRequestsPerMessage), ReplyID: &update.Message.MessageID, }) return } cardmedia := []tg.APIInputMediaPhoto{} errlist := []string{} for _, cardname := range requests { cards, err := mlpapiSearch(cardname) if err != nil { errlist = append(errlist, cardname) } else { if cfg.Log { log.Printf(" -> %d cards", len(cards.Data)) } for _, card := range cards.Data { face, _, _ := getCardEntry(card, 0) cardmedia = append(cardmedia, tg.APIInputMediaPhoto{ Type: "photo", Media: face, }) if card.Type == "Mane" { face, _, _ := getCardEntry(card, 1) cardmedia = append(cardmedia, tg.APIInputMediaPhoto{ Type: "photo", Media: face, }) } } } } if len(cardmedia) > 0 { api.SendAlbum(tg.ClientAlbumData{ ChatID: update.Message.Chat.ChatID, Media: cardmedia, Silent: true, ReplyID: &update.Message.MessageID, }) } if len(errlist) > 0 { api.SendTextMessage(tg.ClientTextMessageData{ ChatID: update.Message.Chat.ChatID, Text: "I couldn't find these cards you mentioned: " + strings.Join(errlist, ", "), ReplyID: &update.Message.MessageID, }) } } // Handle inline callback requests (flipped cards) if update.Callback != nil { if update.Callback.Data != nil && strings.HasPrefix(*update.Callback.Data, "FLIP") { parts := strings.SplitN(*update.Callback.Data, ",", 3) card, err := mlpapiGetCardByID(parts[1]) facenum, _ := strconv.Atoi(parts[2]) if err == nil { face, caption, buttons := getCardEntry(card, facenum) api.EditMedia(tg.ClientEditMediaData{ InlineID: *update.Callback.InlineID, Media: tg.APIInputMediaPhoto{ Type: "photo", Media: face, }, }) api.EditCaption(tg.ClientEditCaptionData{ InlineID: *update.Callback.InlineID, Caption: caption, ReplyMarkup: &tg.APIInlineKeyboardMarkup{ InlineKeyboard: [][]tg.APIInlineKeyboardButton{buttons}, }, }) } } else { fmt.Println("Unknown callback: ", *update.Callback.Data) } api.AnswerCallback(tg.ClientCallbackQueryData{ QueryID: update.Callback.ID, }) } } func getCardRequests(str string) (out []string) { remaining := str for len(remaining) > 1 { nextToken := strings.Index(remaining, "[[") if nextToken < 0 { break } endToken := strings.Index(remaining[nextToken:], "]]") if endToken < 0 { break } out = append(out, remaining[nextToken+2:nextToken+endToken]) remaining = remaining[nextToken+2+endToken:] } return } func getCardEntry(card CardData, flipped int) (string, string, []tg.APIInlineKeyboardButton) { cid := toCardID(card.AllIDs[0]) buttons := []tg.APIInlineKeyboardButton{ { Text: "Ponyhead", URL: "https://ponyhead.com/cards/" + cid, }, } captions := []string{} suffix := "" if card.Type == "Mane" { suffix = "" num := 1 if flipped != 0 { suffix = "b" num = 0 } buttons = append(buttons, tg.APIInlineKeyboardButton{ Text: "🔄", CallbackData: fmt.Sprintf("FLIP,%d,%d", card.GUID, num), }) } face := fmt.Sprintf("http://ponyhead.com/img/cards/%s%s.jpg", cid, suffix) return face, strings.Join(captions, " - "), buttons } func toCardID(id string) string { id = strings.ToLower(id) idx := strings.IndexFunc(id, unicode.IsLetter) num, set := id[:idx], id[idx:] return set + num }