package main import ( "fmt" "os" "strconv" "strings" "git.fromouter.space/hamcha/tg" ) 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 MaxRequestsPerMessage int func main() { MaxRequestsPerMessage, _ = strconv.Atoi(os.Getenv("STAPPA_MAXREQ")) bind := os.Getenv("STAPPA_BIND") if bind == "" { bind = ":8080" } // Set default maxreq if MaxRequestsPerMessage < 1 { MaxRequestsPerMessage = 5 } api = tg.MakeAPIClient(os.Getenv("STAPPA_TOKEN")) api.SetWebhook(os.Getenv("STAPPA_WEBHOOK")) api.HandleWebhook(bind, os.Getenv("STAPPA_PATH"), webhook) } type CardFaceFlipPics struct { Normal string Flipped string Scryfall string Edhrec string MCM string } func webhook(update tg.APIUpdate) { // Handle inline queries (99% of the usage I hope) if update.Inline != nil { query := update.Inline.Query offset, _ := strconv.Atoi(update.Inline.Offset) results, err := scryfallSearch(query, offset) if err != nil { fmt.Println(err) // DO SOMETHING return } nextcard := "" if results.HasMore { nextcard = strconv.Itoa(offset + 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: card.ID, PhotoURL: face.Large, ThumbURL: face.Normal, Title: card.Name, Caption: caption, Width: 672, Height: 936, ReplyMarkup: &tg.APIInlineKeyboardMarkup{ InlineKeyboard: [][]tg.APIInlineKeyboardButton{buttons}, }, } } err = api.AnswerInlineQuery(tg.InlineQueryResponse{ QueryID: update.Inline.QueryID, Results: photos, NextOffset: nextcard, }) 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) > 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), MaxRequestsPerMessage), ReplyID: &update.Message.MessageID, }) return } cardmedia := []tg.APIInputMediaPhoto{} errlist := []string{} for _, cardname := range requests { card, err := scryfallGetCardByName(cardname) if err != nil { errlist = append(errlist, cardname) } else { if card.ImageUris.Large == "" && card.CardFaces != nil { for _, cardface := range card.CardFaces { cardmedia = append(cardmedia, tg.APIInputMediaPhoto{ Type: "photo", Media: cardface.ImageUris.Large, }) } } else { cardmedia = append(cardmedia, tg.APIInputMediaPhoto{ Type: "photo", Media: card.ImageUris.Large, }) } } } 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 := scryfallGetCardByID(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.Large, }, }) 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, facenum int) (CardImage, string, []tg.APIInlineKeyboardButton) { buttons := []tg.APIInlineKeyboardButton{ { Text: "Scryfall", URL: card.ScryfallURI, }, } if card.RelatedUris.Edhrec != "" { buttons = append(buttons, tg.APIInlineKeyboardButton{ Text: "EDHREC", URL: card.RelatedUris.Edhrec, }) } if card.PurchaseUris.Cardmarket != "" { buttons = append(buttons, tg.APIInlineKeyboardButton{ Text: "MCM", URL: card.PurchaseUris.Cardmarket, }) } captions := []string{} if card.EdhrecRank != nil { captions = append(captions, fmt.Sprintf("EDHREC rank: #%d", *card.EdhrecRank)) } if card.Eur != "" { captions = append(captions, fmt.Sprintf("cardmarket: € %s", card.Eur)) } face := card.ImageUris if card.ImageUris.Large == "" && card.CardFaces != nil { face = card.CardFaces[facenum].ImageUris num := 1 if facenum != 0 { num = 0 } buttons = append(buttons, tg.APIInlineKeyboardButton{ Text: "🔄", CallbackData: fmt.Sprintf("FLIP,%s,%d", card.ID, num), }) } return face, strings.Join(captions, " - "), buttons }