From 5f3d0b36a8cbd857f84e6658dbca57fe2e3dba69 Mon Sep 17 00:00:00 2001 From: Jorge Villarreal Date: Tue, 4 Jan 2022 14:17:46 -0600 Subject: [PATCH 1/3] Added Tibia Char Bazaar Auction endpoint. (#23) --- src/TibiaCharbazaarAuctionV3.go | 685 ++++++++++++++++++++++++++++++++ src/webserver.go | 3 + 2 files changed, 688 insertions(+) create mode 100644 src/TibiaCharbazaarAuctionV3.go diff --git a/src/TibiaCharbazaarAuctionV3.go b/src/TibiaCharbazaarAuctionV3.go new file mode 100644 index 00000000..19c62114 --- /dev/null +++ b/src/TibiaCharbazaarAuctionV3.go @@ -0,0 +1,685 @@ +package main + +import ( + "encoding/json" + "github.com/go-resty/resty/v2" + "log" + "strconv" + "strings" + "time" + + "github.com/PuerkitoBio/goquery" + "github.com/gin-gonic/gin" +) + +// AjaxResponseObject - child of AjaxJSONData +type AjaxResponseObject struct { + Data string `json:"Data"` + DataType string `json:"DataType"` + Target string `json:"Target"` +} + +// AjaxJSONData - base response for auction items page links +type AjaxJSONData struct { + AjaxObjects []AjaxResponseObject `json:"AjaxObjects"` +} + +// TibiaCharbazaarAuctionV3 func +func TibiaCharbazaarAuctionV3(c *gin.Context) { + + id := TibiadataStringToIntegerV3(c.Param("id")) + + // Child of Details + type Bid struct { + Type string `json:"type"` + Amount int `json:"amount"` + } + + // Child of Auction + type Details struct { + CharacterName string `json:"characterName"` + Level int `json:"level"` + Vocation string `json:"vocation"` + Gender string `json:"gender"` + World string `json:"world"` + AuctionStart string `json:"auctionStart"` + AuctionEnd string `json:"auctionEnd"` + Bid Bid `json:"bid"` + } + + // Child of Auction + type General struct { + HitPoints int `json:"hitPoints"` + Mana int `json:"mana"` + Capacity int `json:"capacity"` + Speed int `json:"speed"` + Blessings int `json:"blessings"` + Mounts int `json:"mounts"` + Outfits int `json:"outfits"` + Titles int `json:"titles"` + AxeFighting int `json:"axeFighting"` + ClubFighting int `json:"clubFighting"` + DistanceFighting int `json:"distanceFighting"` + Fishing int `json:"fishing"` + FistFighting int `json:"fistFighting"` + MagicLevel int `json:"magicLevel"` + Shielding int `json:"shielding"` + SwordFighting int `json:"swordFighting"` + CreationDate string `json:"creationDate"` + Experience int `json:"experience"` + Gold int `json:"gold"` + AchievementPoints int `json:"achievementPoints"` + RegularWorldTransfer string `json:"regularWorldTransfer"` + CharmExpansion bool `json:"charmExpansion"` + AvailableCharmPoints int `json:"availableCharmPoints"` + SpentCharmPoints int `json:"spentCharmPoints"` + DailyRewardStreak int `json:"dailyRewardStreak"` + HuntingTaskPoints int `json:"huntingTaskPoints"` + PermanentHuntingTaskSlots int `json:"permanentHuntingTaskSlots"` + PermanentPreySlots int `json:"permanentPreySlots"` + PreyWildCards int `json:"preyWildCards"` + Hirelings int `json:"hirelings"` + HirelingJobs int `json:"hirelingJobs"` + HirelingOutfits int `json:"hirelingOutfits"` + ExaltedDust int `json:"exaltedDust"` + } + + // Child of Auction + type Item struct { + Name string `json:"name"` + Amount int `json:"amount"` + } + + // Child of Auction + type Outfit struct { + Name string `json:"name"` + Addon1 bool `json:"addon1"` + Addon2 bool `json:"addon2"` + } + + // Child of Auction + type Blessings struct { + AdventurersBlessing int `json:"adventurersBlessing"` + BloodOfTheMountain int `json:"bloodOfTheMountain"` + EmbraceOfTibia int `json:"embraceOfTibia"` + FireOfTheSuns int `json:"fireOfTheSuns"` + HeartOfTheMountain int `json:"heartOfTheMountain"` + SparkOfThePhoenix int `json:"sparkOfThePhoenix"` + SpiritualShielding int `json:"spiritualShielding"` + TwistOfFate int `json:"twistOfFate"` + WisdomOfSolitude int `json:"wisdomOfSolitude"` + } + + // Child of Auction + type Charm struct { + Name string `json:"name"` + Cost int `json:"cost"` + } + + // Child of Auction + type BestiaryEntry struct { + Name string `json:"name"` + Kills int `json:"kills"` + Step int `json:"step"` + } + + // Child of JSONData + type Auction struct { + Id int `json:"id"` + Details Details `json:"details"` + General General `json:"general"` + ItemSummary []Item `json:"itemSummary"` + StoreItemSummary []Item `json:"storeItemSummary"` + Mounts []string `json:"mounts"` + StoreMounts []string `json:"storeMounts"` + Outfits []Outfit `json:"outfits"` + StoreOutfits []Outfit `json:"storeOutfits"` + Familiars []string `json:"familiars"` + Blessings Blessings `json:"blessings"` + Imbuements []string `json:"imbuements"` + Charms []Charm `json:"charms"` + CompletedCyclopediaMapAreas []string `json:"completedCyclopediaMapAreas"` + CompletedQuestLines []string `json:"completedQuestLines"` + Titles []string `json:"titles"` + Achievements []string `json:"achievements"` + BestiaryProgress []BestiaryEntry `json:"bestiaryProgress"` + } + + // The base includes two levels: Auction and Information + type JSONData struct { + Auction Auction `json:"auction"` + Information Information `json:"information"` + } + + // Getting data with TibiadataHTMLDataCollectorV3 + BoxContentHTML := TibiadataHTMLDataCollectorV3("https://www.tibia.com/charactertrade/?page=details&auctionid=" + + strconv.Itoa(id)) + + // Loading HTML data into ReaderHTML for goquery with NewReader + ReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(BoxContentHTML)) + if err != nil { + log.Fatal(err) + } + + // Extract details section + var _Details Details + ReaderHTML.Find(".Auction").Each(func(index int, s *goquery.Selection) { + detailsHeader := strings.Split(s.Find(".AuctionHeader").Text(), "Level: ") + _Details.CharacterName = detailsHeader[0] + + detailsHeader = strings.Split(detailsHeader[1], "|") + + level := TibiadataStringToIntegerV3(detailsHeader[0]) + + _Details.Level = level + _Details.Vocation = strings.TrimSpace(strings.Split(detailsHeader[1], "Vocation: ")[1]) + _Details.Gender = strings.TrimSpace(detailsHeader[2]) + _Details.World = strings.TrimSpace(strings.Split(detailsHeader[3], "World: ")[1]) + + s.Find(".ShortAuctionData").Each(func(index int, s *goquery.Selection) { + nodes := s.Children().Nodes + + lookupIndex := 0 + hasTimer := strings.EqualFold(nodes[0].Attr[0].Val, "AuctionTimer") + + // In case the auction hasn't ended yet, we need to increase the lookup index. + if hasTimer { + lookupIndex = 1 + } + + auctionStartDate := TibiaDataSanitizeNbspSpaceString(nodes[1+lookupIndex].FirstChild.Data) + auctionStartDate = strings.Split(auctionStartDate, " CET")[0] + ":00 CET" + + auctionEndDate := TibiaDataSanitizeNbspSpaceString(nodes[3+lookupIndex].FirstChild.Data) + auctionEndDate = strings.Split(auctionEndDate, " CET")[0] + ":00 CET" + + _Details.AuctionStart = TibiadataDatetimeV3(auctionStartDate) + _Details.AuctionEnd = TibiadataDatetimeV3(auctionEndDate) + + bidType := strings.Split(nodes[4+lookupIndex].FirstChild.FirstChild.Data, " Bid:")[0] + bidAmount := TibiadataStringToIntegerV3(nodes[4+lookupIndex].LastChild.FirstChild.FirstChild.Data) + + _Details.Bid = Bid{ + Type: bidType, + Amount: bidAmount, + } + }) + }) + + // Extract general section + var _General General + ReaderHTML.Find("#General").Each(func(index int, s *goquery.Selection) { + + // General + generalMap := make(map[string]string) + s.Find(".LabelV").Each(func(index int, s *goquery.Selection) { + generalMap[strings.Split(s.Nodes[0].FirstChild.Data, ":")[0]] = s.Nodes[0].NextSibling.FirstChild.Data + }) + + // Skills + skillsMap := make(map[string]int) + s.Find(".LabelColumn").Each(func(index int, s *goquery.Selection) { + skillsMap[strings.Split(s.Nodes[0].FirstChild.FirstChild.Data, ":")[0]] = + TibiadataStringToIntegerV3(s.Nodes[0].NextSibling.FirstChild.Data) + }) + + _General.HitPoints = TibiadataStringToIntegerV3(generalMap["Hit Points"]) + _General.Mana = TibiadataStringToIntegerV3(generalMap["Mana"]) + _General.Capacity = TibiadataStringToIntegerV3(generalMap["Capacity"]) + _General.Speed = TibiadataStringToIntegerV3(generalMap["Speed"]) + _General.Blessings = TibiadataStringToIntegerV3(strings.Split(generalMap["Blessings"], "/")[0]) + _General.Mounts = TibiadataStringToIntegerV3(generalMap["Mounts"]) + _General.Outfits = TibiadataStringToIntegerV3(generalMap["Outfits"]) + _General.Titles = TibiadataStringToIntegerV3(generalMap["Titles"]) + _General.AxeFighting = skillsMap["Axe Fighting"] + _General.ClubFighting = skillsMap["Club Fighting"] + _General.DistanceFighting = skillsMap["Distance Fighting"] + _General.Fishing = skillsMap["Fishing"] + _General.FistFighting = skillsMap["Fist Fighting"] + _General.MagicLevel = skillsMap["Magic Level"] + _General.Shielding = skillsMap["Shielding"] + _General.SwordFighting = skillsMap["Sword Fighting"] + _General.CreationDate = TibiadataDatetimeV3(generalMap["Creation Date"]) + _General.Experience = TibiadataStringToIntegerV3(generalMap["Experience"]) + _General.Gold = TibiadataStringToIntegerV3(generalMap["Gold"]) + _General.AchievementPoints = TibiadataStringToIntegerV3(generalMap["Achievement Points"]) + _General.RegularWorldTransfer = generalMap["Regular World Transfer"] + _General.CharmExpansion = strings.EqualFold(generalMap["Charm Expansion"], "yes") + _General.AvailableCharmPoints = TibiadataStringToIntegerV3(generalMap["Available Charm Points"]) + _General.SpentCharmPoints = TibiadataStringToIntegerV3(generalMap["Spent Charm Points"]) + _General.DailyRewardStreak = TibiadataStringToIntegerV3(generalMap["Daily Reward Streak"]) + _General.HuntingTaskPoints = TibiadataStringToIntegerV3(generalMap["Hunting Task Points"]) + _General.PermanentHuntingTaskSlots = TibiadataStringToIntegerV3(generalMap["Permanent Hunting Task Slots"]) + _General.PermanentPreySlots = TibiadataStringToIntegerV3(generalMap["Permanent Prey Slots"]) + _General.PreyWildCards = TibiadataStringToIntegerV3(generalMap["Prey Wildcards"]) + _General.Hirelings = TibiadataStringToIntegerV3(generalMap["Hirelings"]) + _General.HirelingJobs = TibiadataStringToIntegerV3(generalMap["Hireling Jobs"]) + _General.HirelingOutfits = TibiadataStringToIntegerV3(generalMap["Hireling Outfits"]) + _General.ExaltedDust = TibiadataStringToIntegerV3(strings.Split(generalMap["Exalted Dust"], "/")[0]) + }) + + // Extract items summary + var _ItemSummary []Item + ReaderHTML.Find("#ItemSummary").Each(func(index int, s *goquery.Selection) { + + for k, v := range ParseItems(s) { + _ItemSummary = append(_ItemSummary, Item{Name: k, Amount: v}) + } + + totalPages := s.Find(".PageLink").Size() + if totalPages > 1 { + // Fetch missing pages using links + for i := 2; i <= totalPages; i++ { + itemPage := AjaxJSONDataCollectorV3( + "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + + "&type=0¤tpage=" + strconv.Itoa(i)) + ItemPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(itemPage)) + if err != nil { + log.Fatal(err) + } + for k, v := range ParseItems(ItemPageReaderHTML.Contents()) { + _ItemSummary = append(_ItemSummary, Item{Name: k, Amount: v}) + } + } + } + }) + + // Extract store items summary + var _StoreItemSummary []Item + ReaderHTML.Find("#StoreItemSummary").Each(func(index int, s *goquery.Selection) { + + for k, v := range ParseItems(s) { + _StoreItemSummary = append(_StoreItemSummary, Item{Name: k, Amount: v}) + } + + totalPages := s.Find(".PageLink").Size() + if totalPages > 1 { + // Fetch missing pages using links + for i := 2; i <= totalPages; i++ { + itemPage := AjaxJSONDataCollectorV3( + "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + + "&type=1¤tpage=" + strconv.Itoa(i)) + ItemPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(itemPage)) + if err != nil { + log.Fatal(err) + } + for k, v := range ParseItems(ItemPageReaderHTML.Contents()) { + _StoreItemSummary = append(_StoreItemSummary, Item{Name: k, Amount: v}) + } + } + } + }) + + // Extract mounts + var _Mounts []string + ReaderHTML.Find("#Mounts").Each(func(index int, s *goquery.Selection) { + + _Mounts = append(_Mounts, ParseMounts(s)...) + + totalPages := s.Find(".PageLink").Size() + if totalPages > 1 { + // Fetch missing pages using links + for i := 2; i <= totalPages; i++ { + mountsPage := AjaxJSONDataCollectorV3( + "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + + "&type=2¤tpage=" + strconv.Itoa(i)) + MountsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(mountsPage)) + if err != nil { + log.Fatal(err) + } + _Mounts = append(_Mounts, ParseMounts(MountsPageReaderHTML.Contents())...) + } + } + }) + + // Extract store mounts + var _StoreMounts []string + ReaderHTML.Find("#StoreMounts").Each(func(index int, s *goquery.Selection) { + + _StoreMounts = append(_StoreMounts, ParseMounts(s)...) + + totalPages := s.Find(".PageLink").Size() + if totalPages > 1 { + // Fetch missing pages using links + for i := 2; i <= totalPages; i++ { + mountsPage := AjaxJSONDataCollectorV3( + "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + + "&type=3¤tpage=" + strconv.Itoa(i)) + MountsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(mountsPage)) + if err != nil { + log.Fatal(err) + } + _StoreMounts = append(_StoreMounts, ParseMounts(MountsPageReaderHTML.Contents())...) + } + } + }) + + // Extract outfits + var _Outfits []Outfit + ReaderHTML.Find("#Outfits").Each(func(index int, s *goquery.Selection) { + for k, v := range ParseOutfits(s) { + _Outfits = append(_Outfits, Outfit{ + Name: k, + Addon1: v[0], + Addon2: v[1], + }) + } + totalPages := s.Find(".PageLink").Size() + if totalPages > 1 { + // Fetch missing pages using links + for i := 2; i <= totalPages; i++ { + outfitsPage := AjaxJSONDataCollectorV3( + "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + + "&type=4¤tpage=" + strconv.Itoa(i)) + OutfitsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(outfitsPage)) + if err != nil { + log.Fatal(err) + } + for k, v := range ParseOutfits(OutfitsPageReaderHTML.Contents()) { + _Outfits = append(_Outfits, Outfit{ + Name: k, + Addon1: v[0], + Addon2: v[1], + }) + } + } + } + }) + + // Extract store outfits + var _StoreOutfits []Outfit + ReaderHTML.Find("#StoreOutfits").Each(func(index int, s *goquery.Selection) { + for k, v := range ParseOutfits(s) { + _Outfits = append(_Outfits, Outfit{ + Name: k, + Addon1: v[0], + Addon2: v[1], + }) + } + totalPages := s.Find(".PageLink").Size() + if totalPages > 1 { + // Fetch missing pages using links + for i := 2; i <= totalPages; i++ { + outfitsPage := AjaxJSONDataCollectorV3( + "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + + "&type=5¤tpage=" + strconv.Itoa(i)) + OutfitsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(outfitsPage)) + if err != nil { + log.Fatal(err) + } + for k, v := range ParseOutfits(OutfitsPageReaderHTML.Contents()) { + _Outfits = append(_Outfits, Outfit{ + Name: k, + Addon1: v[0], + Addon2: v[1], + }) + } + } + } + }) + + // Extract familiars + var _Familiars []string + ReaderHTML.Find("#Familiars").Each(func(index int, s *goquery.Selection) { + s.Find(".CVIcon").Each(func(index int, s *goquery.Selection) { + familiarName, exists := s.Attr("title") + if exists { + _Familiars = append(_Familiars, familiarName) + } + }) + }) + + // Extract blessings + var _Blessings Blessings + ReaderHTML.Find("#Blessings").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + blessingsAmount := TibiadataStringToIntegerV3(strings.Split(node.FirstChild.Data, " x")[0]) + switch blessingName := node.NextSibling.FirstChild.Data; blessingName { + case "Adventurer's Blessing": + _Blessings.AdventurersBlessing = blessingsAmount + case "Blood of the Mountain": + _Blessings.BloodOfTheMountain = blessingsAmount + case "Embrace of Tibia": + _Blessings.EmbraceOfTibia = blessingsAmount + case "Fire of the Suns": + _Blessings.FireOfTheSuns = blessingsAmount + case "Heart of the Mountain": + _Blessings.HeartOfTheMountain = blessingsAmount + case "Spark of the Phoenix": + _Blessings.SparkOfThePhoenix = blessingsAmount + case "Spiritual Shielding": + _Blessings.SpiritualShielding = blessingsAmount + case "Twist of Fate": + _Blessings.TwistOfFate = blessingsAmount + case "Wisdom of Solitude": + _Blessings.WisdomOfSolitude = blessingsAmount + } + }) + }) + + // Extract Imbuements + var _Imbuements []string + ReaderHTML.Find("#Imbuements").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + if strings.Contains(node.FirstChild.Data, "No imbuements.") { + return + } + if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { + _Imbuements = append(_Imbuements, strings.TrimSpace(node.FirstChild.Data)) + } + }) + }) + + // Extract Charms + var _Charms []Charm + ReaderHTML.Find("#Charms").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + if strings.Contains(node.FirstChild.Data, "No charms.") { + return + } + if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { + _Charms = append(_Charms, Charm{ + Cost: TibiadataStringToIntegerV3(node.FirstChild.Data), + Name: node.NextSibling.FirstChild.Data, + }) + } + }) + }) + + // Extract Completed Cyclopedia Map Areas + var _CompletedCyclopediaMapAreas []string + ReaderHTML.Find("#CompletedCyclopediaMapAreas").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + if strings.Contains(node.FirstChild.Data, "No areas explored.") { + return + } + if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { + _CompletedCyclopediaMapAreas = append(_CompletedCyclopediaMapAreas, strings.TrimSpace(node.FirstChild.Data)) + } + }) + }) + + // Extract Completed Quest Lines + var _CompletedQuestLines []string + ReaderHTML.Find("#CompletedQuestLines").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { + _CompletedQuestLines = append(_CompletedQuestLines, strings.TrimSpace(node.FirstChild.Data)) + } + }) + }) + + // Extract Titles + var _Titles []string + ReaderHTML.Find("#Titles").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { + _Titles = append(_Titles, strings.TrimSpace(node.FirstChild.Data)) + } + }) + }) + + // Extract Achievements + var _Achievements []string + ReaderHTML.Find("#Achievements").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { + _Achievements = append(_Achievements, strings.TrimSpace(node.FirstChild.Data)) + } + }) + }) + + // Extract Bestiary Progress + var _BestiaryProgress []BestiaryEntry + ReaderHTML.Find("#BestiaryProgress").Each(func(index int, s *goquery.Selection) { + s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + node := s.Nodes[0].FirstChild + if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { + _BestiaryProgress = append(_BestiaryProgress, BestiaryEntry{ + Step: TibiadataStringToIntegerV3(node.FirstChild.Data), + Kills: TibiadataStringToIntegerV3(strings.Split(node.NextSibling.FirstChild.Data, " x")[0]), + Name: node.NextSibling.NextSibling.FirstChild.Data, + }) + } + }) + }) + + jsonData := JSONData{ + Auction: Auction{ + Id: id, + Details: _Details, + General: _General, + ItemSummary: _ItemSummary, + StoreItemSummary: _StoreItemSummary, + Mounts: _Mounts, + StoreMounts: _StoreMounts, + Outfits: _Outfits, + StoreOutfits: _StoreOutfits, + Familiars: _Familiars, + Blessings: _Blessings, + Imbuements: _Imbuements, + Charms: _Charms, + CompletedCyclopediaMapAreas: _CompletedCyclopediaMapAreas, + CompletedQuestLines: _CompletedQuestLines, + Titles: _Titles, + Achievements: _Achievements, + BestiaryProgress: _BestiaryProgress, + }, + Information: Information{ + APIVersion: TibiadataAPIversion, + Timestamp: TibiadataDatetimeV3(""), + }, + } + + // return jsonData + TibiaDataAPIHandleSuccessResponse(c, "TibiaCharbazaarAuctionV3", jsonData) +} + +func ParseItems(s *goquery.Selection) map[string]int { + m := make(map[string]int) + s.Find(".CVIconObject").Each(func(index int, s *goquery.Selection) { + + itemTitle, exists := s.Attr("title") + + if exists { + var itemAmount int + var itemName string + + nodes := s.Find(".ObjectAmount").First().Nodes + if nodes == nil { + itemName = strings.Split(itemTitle, "\n")[0] + itemAmount = 1 + } else { + temp := strings.SplitN(itemTitle, "x ", 2) + itemName = strings.Split(temp[1], "\n")[0] + itemAmount = TibiadataStringToIntegerV3(temp[0]) + } + m[itemName] = itemAmount + } + }) + return m +} + +func ParseOutfits(s *goquery.Selection) map[string][]bool { + m := make(map[string][]bool) + s.Find(".CVIcon").Each(func(index int, s *goquery.Selection) { + outfitTitle, exists := s.Attr("title") + if exists { + outfitName := strings.Split(outfitTitle, " (")[0] + hasAddon1 := strings.Contains(outfitTitle, "addon 1") + hasAddon2 := strings.Contains(outfitTitle, "addon 2") + m[outfitName] = []bool{hasAddon1, hasAddon2} + } + }) + return m +} + +func ParseMounts(s *goquery.Selection) []string { + var mountsList []string + s.Find(".CVIcon").Each(func(index int, s *goquery.Selection) { + mountTitle, exists := s.Attr("title") + if exists { + mountsList = append(mountsList, mountTitle) + } + }) + return mountsList +} + +func AjaxJSONDataCollectorV3(TibiaURL string) string { + + // Setting up resty client + client := resty.New() + + // Set Debug if enabled by TibiadataDebug var + if TibiadataDebug { + client.SetDebug(true) + } + + // Set client timeout and retry + client.SetTimeout(5 * time.Second) + client.SetRetryCount(2) + + // Set headers for all requests + client.SetHeaders(map[string]string{ + "X-Requested-With": "XMLHttpRequest", + "Content-Type": "application/json", + "User-Agent": TibiadataUserAgent, + }) + + // Enabling Content length value for all request + client.SetContentLength(true) + + // Disable redirection of client (so we skip parsing maintenance page) + client.SetRedirectPolicy(resty.NoRedirectPolicy()) + + res, err := client.R().Get(TibiaURL) + if err != nil { + log.Printf("[error] AjaxJSONDataCollectorV3 (URL: %s) in resp1: %s", TibiaURL, err) + } + + // Checking if status is something else than 200 + if res.StatusCode() != 200 { + log.Printf("[warni] AjaxJSONDataCollectorV3 (URL: %s) status code: %s", TibiaURL, res.Status()) + + // Check if page is in maintenance mode + if res.StatusCode() == 302 { + log.Printf("[info] AjaxJSONDataCollectorV3 (URL: %s): Page tibia.com returns 302, probably maintenance mode enabled?", TibiaURL) + } + } + + var result AjaxJSONData + err = json.Unmarshal(res.Body(), &result) + if err != nil { + log.Printf("[error] AjaxJSONDataCollectorV3 (URL: %s) in deserialization process: %s", TibiaURL, err) + } + + // Return of extracted html to functions + return result.AjaxObjects[0].Data +} diff --git a/src/webserver.go b/src/webserver.go index 8c987fda..faf00e5d 100644 --- a/src/webserver.go +++ b/src/webserver.go @@ -118,6 +118,9 @@ func runWebServer() { // Tibia worlds v3.GET("/worlds", tibiaWorldsOverviewV3) v3.GET("/worlds/world/:world", tibiaWorldsWorldV3) + + // TibiaBazaarAuctionV3 + v3.GET("/charbazaar/auction/:id", TibiaCharbazaarAuctionV3) } // container version details endpoint From fc254ee50c106cb59c6b783d22d235ff1a44d1a2 Mon Sep 17 00:00:00 2001 From: Jorge Villarreal Date: Tue, 4 Jan 2022 18:21:40 -0600 Subject: [PATCH 2/3] Variable renaming and use of constants. (#28) --- src/TibiaCharbazaarAuctionV3.go | 318 ++++++++++++++++---------------- 1 file changed, 160 insertions(+), 158 deletions(-) diff --git a/src/TibiaCharbazaarAuctionV3.go b/src/TibiaCharbazaarAuctionV3.go index 19c62114..b6b931c7 100644 --- a/src/TibiaCharbazaarAuctionV3.go +++ b/src/TibiaCharbazaarAuctionV3.go @@ -12,18 +12,6 @@ import ( "github.com/gin-gonic/gin" ) -// AjaxResponseObject - child of AjaxJSONData -type AjaxResponseObject struct { - Data string `json:"Data"` - DataType string `json:"DataType"` - Target string `json:"Target"` -} - -// AjaxJSONData - base response for auction items page links -type AjaxJSONData struct { - AjaxObjects []AjaxResponseObject `json:"AjaxObjects"` -} - // TibiaCharbazaarAuctionV3 func func TibiaCharbazaarAuctionV3(c *gin.Context) { @@ -162,19 +150,19 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { } // Extract details section - var _Details Details + var details Details ReaderHTML.Find(".Auction").Each(func(index int, s *goquery.Selection) { detailsHeader := strings.Split(s.Find(".AuctionHeader").Text(), "Level: ") - _Details.CharacterName = detailsHeader[0] + details.CharacterName = detailsHeader[0] detailsHeader = strings.Split(detailsHeader[1], "|") level := TibiadataStringToIntegerV3(detailsHeader[0]) - _Details.Level = level - _Details.Vocation = strings.TrimSpace(strings.Split(detailsHeader[1], "Vocation: ")[1]) - _Details.Gender = strings.TrimSpace(detailsHeader[2]) - _Details.World = strings.TrimSpace(strings.Split(detailsHeader[3], "World: ")[1]) + details.Level = level + details.Vocation = strings.TrimSpace(strings.Split(detailsHeader[1], "Vocation: ")[1]) + details.Gender = strings.TrimSpace(detailsHeader[2]) + details.World = strings.TrimSpace(strings.Split(detailsHeader[3], "World: ")[1]) s.Find(".ShortAuctionData").Each(func(index int, s *goquery.Selection) { nodes := s.Children().Nodes @@ -193,13 +181,13 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { auctionEndDate := TibiaDataSanitizeNbspSpaceString(nodes[3+lookupIndex].FirstChild.Data) auctionEndDate = strings.Split(auctionEndDate, " CET")[0] + ":00 CET" - _Details.AuctionStart = TibiadataDatetimeV3(auctionStartDate) - _Details.AuctionEnd = TibiadataDatetimeV3(auctionEndDate) + details.AuctionStart = TibiadataDatetimeV3(auctionStartDate) + details.AuctionEnd = TibiadataDatetimeV3(auctionEndDate) bidType := strings.Split(nodes[4+lookupIndex].FirstChild.FirstChild.Data, " Bid:")[0] bidAmount := TibiadataStringToIntegerV3(nodes[4+lookupIndex].LastChild.FirstChild.FirstChild.Data) - _Details.Bid = Bid{ + details.Bid = Bid{ Type: bidType, Amount: bidAmount, } @@ -207,7 +195,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract general section - var _General General + var general General ReaderHTML.Find("#General").Each(func(index int, s *goquery.Selection) { // General @@ -223,160 +211,150 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { TibiadataStringToIntegerV3(s.Nodes[0].NextSibling.FirstChild.Data) }) - _General.HitPoints = TibiadataStringToIntegerV3(generalMap["Hit Points"]) - _General.Mana = TibiadataStringToIntegerV3(generalMap["Mana"]) - _General.Capacity = TibiadataStringToIntegerV3(generalMap["Capacity"]) - _General.Speed = TibiadataStringToIntegerV3(generalMap["Speed"]) - _General.Blessings = TibiadataStringToIntegerV3(strings.Split(generalMap["Blessings"], "/")[0]) - _General.Mounts = TibiadataStringToIntegerV3(generalMap["Mounts"]) - _General.Outfits = TibiadataStringToIntegerV3(generalMap["Outfits"]) - _General.Titles = TibiadataStringToIntegerV3(generalMap["Titles"]) - _General.AxeFighting = skillsMap["Axe Fighting"] - _General.ClubFighting = skillsMap["Club Fighting"] - _General.DistanceFighting = skillsMap["Distance Fighting"] - _General.Fishing = skillsMap["Fishing"] - _General.FistFighting = skillsMap["Fist Fighting"] - _General.MagicLevel = skillsMap["Magic Level"] - _General.Shielding = skillsMap["Shielding"] - _General.SwordFighting = skillsMap["Sword Fighting"] - _General.CreationDate = TibiadataDatetimeV3(generalMap["Creation Date"]) - _General.Experience = TibiadataStringToIntegerV3(generalMap["Experience"]) - _General.Gold = TibiadataStringToIntegerV3(generalMap["Gold"]) - _General.AchievementPoints = TibiadataStringToIntegerV3(generalMap["Achievement Points"]) - _General.RegularWorldTransfer = generalMap["Regular World Transfer"] - _General.CharmExpansion = strings.EqualFold(generalMap["Charm Expansion"], "yes") - _General.AvailableCharmPoints = TibiadataStringToIntegerV3(generalMap["Available Charm Points"]) - _General.SpentCharmPoints = TibiadataStringToIntegerV3(generalMap["Spent Charm Points"]) - _General.DailyRewardStreak = TibiadataStringToIntegerV3(generalMap["Daily Reward Streak"]) - _General.HuntingTaskPoints = TibiadataStringToIntegerV3(generalMap["Hunting Task Points"]) - _General.PermanentHuntingTaskSlots = TibiadataStringToIntegerV3(generalMap["Permanent Hunting Task Slots"]) - _General.PermanentPreySlots = TibiadataStringToIntegerV3(generalMap["Permanent Prey Slots"]) - _General.PreyWildCards = TibiadataStringToIntegerV3(generalMap["Prey Wildcards"]) - _General.Hirelings = TibiadataStringToIntegerV3(generalMap["Hirelings"]) - _General.HirelingJobs = TibiadataStringToIntegerV3(generalMap["Hireling Jobs"]) - _General.HirelingOutfits = TibiadataStringToIntegerV3(generalMap["Hireling Outfits"]) - _General.ExaltedDust = TibiadataStringToIntegerV3(strings.Split(generalMap["Exalted Dust"], "/")[0]) + general.HitPoints = TibiadataStringToIntegerV3(generalMap["Hit Points"]) + general.Mana = TibiadataStringToIntegerV3(generalMap["Mana"]) + general.Capacity = TibiadataStringToIntegerV3(generalMap["Capacity"]) + general.Speed = TibiadataStringToIntegerV3(generalMap["Speed"]) + general.Blessings = TibiadataStringToIntegerV3(strings.Split(generalMap["Blessings"], "/")[0]) + general.Mounts = TibiadataStringToIntegerV3(generalMap["Mounts"]) + general.Outfits = TibiadataStringToIntegerV3(generalMap["Outfits"]) + general.Titles = TibiadataStringToIntegerV3(generalMap["Titles"]) + general.AxeFighting = skillsMap["Axe Fighting"] + general.ClubFighting = skillsMap["Club Fighting"] + general.DistanceFighting = skillsMap["Distance Fighting"] + general.Fishing = skillsMap["Fishing"] + general.FistFighting = skillsMap["Fist Fighting"] + general.MagicLevel = skillsMap["Magic Level"] + general.Shielding = skillsMap["Shielding"] + general.SwordFighting = skillsMap["Sword Fighting"] + general.CreationDate = TibiadataDatetimeV3(generalMap["Creation Date"]) + general.Experience = TibiadataStringToIntegerV3(generalMap["Experience"]) + general.Gold = TibiadataStringToIntegerV3(generalMap["Gold"]) + general.AchievementPoints = TibiadataStringToIntegerV3(generalMap["Achievement Points"]) + general.RegularWorldTransfer = generalMap["Regular World Transfer"] + general.CharmExpansion = strings.EqualFold(generalMap["Charm Expansion"], "yes") + general.AvailableCharmPoints = TibiadataStringToIntegerV3(generalMap["Available Charm Points"]) + general.SpentCharmPoints = TibiadataStringToIntegerV3(generalMap["Spent Charm Points"]) + general.DailyRewardStreak = TibiadataStringToIntegerV3(generalMap["Daily Reward Streak"]) + general.HuntingTaskPoints = TibiadataStringToIntegerV3(generalMap["Hunting Task Points"]) + general.PermanentHuntingTaskSlots = TibiadataStringToIntegerV3(generalMap["Permanent Hunting Task Slots"]) + general.PermanentPreySlots = TibiadataStringToIntegerV3(generalMap["Permanent Prey Slots"]) + general.PreyWildCards = TibiadataStringToIntegerV3(generalMap["Prey Wildcards"]) + general.Hirelings = TibiadataStringToIntegerV3(generalMap["Hirelings"]) + general.HirelingJobs = TibiadataStringToIntegerV3(generalMap["Hireling Jobs"]) + general.HirelingOutfits = TibiadataStringToIntegerV3(generalMap["Hireling Outfits"]) + general.ExaltedDust = TibiadataStringToIntegerV3(strings.Split(generalMap["Exalted Dust"], "/")[0]) }) // Extract items summary - var _ItemSummary []Item + var itemSummary []Item ReaderHTML.Find("#ItemSummary").Each(func(index int, s *goquery.Selection) { for k, v := range ParseItems(s) { - _ItemSummary = append(_ItemSummary, Item{Name: k, Amount: v}) + itemSummary = append(itemSummary, Item{Name: k, Amount: v}) } - totalPages := s.Find(".PageLink").Size() + totalPages := s.Find(PageLinkSelector).Size() if totalPages > 1 { // Fetch missing pages using links - for i := 2; i <= totalPages; i++ { - itemPage := AjaxJSONDataCollectorV3( - "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + - "&type=0¤tpage=" + strconv.Itoa(i)) + for pageIndex := 2; pageIndex <= totalPages; pageIndex++ { + itemPage := AjaxJSONDataCollectorV3(id, ItemSummarySection, pageIndex) ItemPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(itemPage)) if err != nil { log.Fatal(err) } for k, v := range ParseItems(ItemPageReaderHTML.Contents()) { - _ItemSummary = append(_ItemSummary, Item{Name: k, Amount: v}) + itemSummary = append(itemSummary, Item{Name: k, Amount: v}) } } } }) // Extract store items summary - var _StoreItemSummary []Item + var storeItemSummary []Item ReaderHTML.Find("#StoreItemSummary").Each(func(index int, s *goquery.Selection) { for k, v := range ParseItems(s) { - _StoreItemSummary = append(_StoreItemSummary, Item{Name: k, Amount: v}) + storeItemSummary = append(storeItemSummary, Item{Name: k, Amount: v}) } - totalPages := s.Find(".PageLink").Size() + totalPages := s.Find(PageLinkSelector).Size() if totalPages > 1 { // Fetch missing pages using links - for i := 2; i <= totalPages; i++ { - itemPage := AjaxJSONDataCollectorV3( - "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + - "&type=1¤tpage=" + strconv.Itoa(i)) + for pageIndex := 2; pageIndex <= totalPages; pageIndex++ { + itemPage := AjaxJSONDataCollectorV3(id, StoreItemSummarySection, pageIndex) ItemPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(itemPage)) if err != nil { log.Fatal(err) } for k, v := range ParseItems(ItemPageReaderHTML.Contents()) { - _StoreItemSummary = append(_StoreItemSummary, Item{Name: k, Amount: v}) + storeItemSummary = append(storeItemSummary, Item{Name: k, Amount: v}) } } } }) // Extract mounts - var _Mounts []string + var mounts []string ReaderHTML.Find("#Mounts").Each(func(index int, s *goquery.Selection) { - _Mounts = append(_Mounts, ParseMounts(s)...) + mounts = append(mounts, ParseMounts(s)...) - totalPages := s.Find(".PageLink").Size() + totalPages := s.Find(PageLinkSelector).Size() if totalPages > 1 { // Fetch missing pages using links - for i := 2; i <= totalPages; i++ { - mountsPage := AjaxJSONDataCollectorV3( - "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + - "&type=2¤tpage=" + strconv.Itoa(i)) + for pageIndex := 2; pageIndex <= totalPages; pageIndex++ { + mountsPage := AjaxJSONDataCollectorV3(id, MountsSection, pageIndex) MountsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(mountsPage)) if err != nil { log.Fatal(err) } - _Mounts = append(_Mounts, ParseMounts(MountsPageReaderHTML.Contents())...) + mounts = append(mounts, ParseMounts(MountsPageReaderHTML.Contents())...) } } }) // Extract store mounts - var _StoreMounts []string + var storeMounts []string ReaderHTML.Find("#StoreMounts").Each(func(index int, s *goquery.Selection) { - _StoreMounts = append(_StoreMounts, ParseMounts(s)...) + storeMounts = append(storeMounts, ParseMounts(s)...) - totalPages := s.Find(".PageLink").Size() + totalPages := s.Find(PageLinkSelector).Size() if totalPages > 1 { // Fetch missing pages using links - for i := 2; i <= totalPages; i++ { - mountsPage := AjaxJSONDataCollectorV3( - "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + - "&type=3¤tpage=" + strconv.Itoa(i)) + for pageIndex := 2; pageIndex <= totalPages; pageIndex++ { + mountsPage := AjaxJSONDataCollectorV3(id, StoreMountsSection, pageIndex) MountsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(mountsPage)) if err != nil { log.Fatal(err) } - _StoreMounts = append(_StoreMounts, ParseMounts(MountsPageReaderHTML.Contents())...) + storeMounts = append(storeMounts, ParseMounts(MountsPageReaderHTML.Contents())...) } } }) // Extract outfits - var _Outfits []Outfit + var outfits []Outfit ReaderHTML.Find("#Outfits").Each(func(index int, s *goquery.Selection) { for k, v := range ParseOutfits(s) { - _Outfits = append(_Outfits, Outfit{ + outfits = append(outfits, Outfit{ Name: k, Addon1: v[0], Addon2: v[1], }) } - totalPages := s.Find(".PageLink").Size() + totalPages := s.Find(PageLinkSelector).Size() if totalPages > 1 { // Fetch missing pages using links - for i := 2; i <= totalPages; i++ { - outfitsPage := AjaxJSONDataCollectorV3( - "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + - "&type=4¤tpage=" + strconv.Itoa(i)) + for pageIndex := 2; pageIndex <= totalPages; pageIndex++ { + outfitsPage := AjaxJSONDataCollectorV3(id, OutfitsSection, pageIndex) OutfitsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(outfitsPage)) if err != nil { log.Fatal(err) } for k, v := range ParseOutfits(OutfitsPageReaderHTML.Contents()) { - _Outfits = append(_Outfits, Outfit{ + outfits = append(outfits, Outfit{ Name: k, Addon1: v[0], Addon2: v[1], @@ -387,28 +365,26 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract store outfits - var _StoreOutfits []Outfit + var storeOutfits []Outfit ReaderHTML.Find("#StoreOutfits").Each(func(index int, s *goquery.Selection) { for k, v := range ParseOutfits(s) { - _Outfits = append(_Outfits, Outfit{ + outfits = append(outfits, Outfit{ Name: k, Addon1: v[0], Addon2: v[1], }) } - totalPages := s.Find(".PageLink").Size() + totalPages := s.Find(PageLinkSelector).Size() if totalPages > 1 { // Fetch missing pages using links - for i := 2; i <= totalPages; i++ { - outfitsPage := AjaxJSONDataCollectorV3( - "https://www.tibia.com/websiteservices/handle_charactertrades.php?auctionid=" + strconv.Itoa(id) + - "&type=5¤tpage=" + strconv.Itoa(i)) + for pageIndex := 2; pageIndex <= totalPages; pageIndex++ { + outfitsPage := AjaxJSONDataCollectorV3(id, StoreOutfitsSection, pageIndex) OutfitsPageReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(outfitsPage)) if err != nil { log.Fatal(err) } for k, v := range ParseOutfits(OutfitsPageReaderHTML.Contents()) { - _Outfits = append(_Outfits, Outfit{ + outfits = append(outfits, Outfit{ Name: k, Addon1: v[0], Addon2: v[1], @@ -419,69 +395,69 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract familiars - var _Familiars []string + var familiars []string ReaderHTML.Find("#Familiars").Each(func(index int, s *goquery.Selection) { - s.Find(".CVIcon").Each(func(index int, s *goquery.Selection) { + s.Find(CVIconSelector).Each(func(index int, s *goquery.Selection) { familiarName, exists := s.Attr("title") if exists { - _Familiars = append(_Familiars, familiarName) + familiars = append(familiars, familiarName) } }) }) // Extract blessings - var _Blessings Blessings + var blessings Blessings ReaderHTML.Find("#Blessings").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild blessingsAmount := TibiadataStringToIntegerV3(strings.Split(node.FirstChild.Data, " x")[0]) switch blessingName := node.NextSibling.FirstChild.Data; blessingName { case "Adventurer's Blessing": - _Blessings.AdventurersBlessing = blessingsAmount + blessings.AdventurersBlessing = blessingsAmount case "Blood of the Mountain": - _Blessings.BloodOfTheMountain = blessingsAmount + blessings.BloodOfTheMountain = blessingsAmount case "Embrace of Tibia": - _Blessings.EmbraceOfTibia = blessingsAmount + blessings.EmbraceOfTibia = blessingsAmount case "Fire of the Suns": - _Blessings.FireOfTheSuns = blessingsAmount + blessings.FireOfTheSuns = blessingsAmount case "Heart of the Mountain": - _Blessings.HeartOfTheMountain = blessingsAmount + blessings.HeartOfTheMountain = blessingsAmount case "Spark of the Phoenix": - _Blessings.SparkOfThePhoenix = blessingsAmount + blessings.SparkOfThePhoenix = blessingsAmount case "Spiritual Shielding": - _Blessings.SpiritualShielding = blessingsAmount + blessings.SpiritualShielding = blessingsAmount case "Twist of Fate": - _Blessings.TwistOfFate = blessingsAmount + blessings.TwistOfFate = blessingsAmount case "Wisdom of Solitude": - _Blessings.WisdomOfSolitude = blessingsAmount + blessings.WisdomOfSolitude = blessingsAmount } }) }) // Extract Imbuements - var _Imbuements []string + var imbuements []string ReaderHTML.Find("#Imbuements").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if strings.Contains(node.FirstChild.Data, "No imbuements.") { return } if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - _Imbuements = append(_Imbuements, strings.TrimSpace(node.FirstChild.Data)) + imbuements = append(imbuements, strings.TrimSpace(node.FirstChild.Data)) } }) }) // Extract Charms - var _Charms []Charm + var charms []Charm ReaderHTML.Find("#Charms").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if strings.Contains(node.FirstChild.Data, "No charms.") { return } if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - _Charms = append(_Charms, Charm{ + charms = append(charms, Charm{ Cost: TibiadataStringToIntegerV3(node.FirstChild.Data), Name: node.NextSibling.FirstChild.Data, }) @@ -490,59 +466,59 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract Completed Cyclopedia Map Areas - var _CompletedCyclopediaMapAreas []string + var completedCyclopediaMapAreas []string ReaderHTML.Find("#CompletedCyclopediaMapAreas").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if strings.Contains(node.FirstChild.Data, "No areas explored.") { return } if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - _CompletedCyclopediaMapAreas = append(_CompletedCyclopediaMapAreas, strings.TrimSpace(node.FirstChild.Data)) + completedCyclopediaMapAreas = append(completedCyclopediaMapAreas, strings.TrimSpace(node.FirstChild.Data)) } }) }) // Extract Completed Quest Lines - var _CompletedQuestLines []string + var completedQuestLines []string ReaderHTML.Find("#CompletedQuestLines").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - _CompletedQuestLines = append(_CompletedQuestLines, strings.TrimSpace(node.FirstChild.Data)) + completedQuestLines = append(completedQuestLines, strings.TrimSpace(node.FirstChild.Data)) } }) }) // Extract Titles - var _Titles []string + var titles []string ReaderHTML.Find("#Titles").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - _Titles = append(_Titles, strings.TrimSpace(node.FirstChild.Data)) + titles = append(titles, strings.TrimSpace(node.FirstChild.Data)) } }) }) // Extract Achievements - var _Achievements []string + var achievements []string ReaderHTML.Find("#Achievements").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - _Achievements = append(_Achievements, strings.TrimSpace(node.FirstChild.Data)) + achievements = append(achievements, strings.TrimSpace(node.FirstChild.Data)) } }) }) // Extract Bestiary Progress - var _BestiaryProgress []BestiaryEntry + var bestiaryProgress []BestiaryEntry ReaderHTML.Find("#BestiaryProgress").Each(func(index int, s *goquery.Selection) { - s.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) { + s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - _BestiaryProgress = append(_BestiaryProgress, BestiaryEntry{ + bestiaryProgress = append(bestiaryProgress, BestiaryEntry{ Step: TibiadataStringToIntegerV3(node.FirstChild.Data), Kills: TibiadataStringToIntegerV3(strings.Split(node.NextSibling.FirstChild.Data, " x")[0]), Name: node.NextSibling.NextSibling.FirstChild.Data, @@ -554,23 +530,23 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { jsonData := JSONData{ Auction: Auction{ Id: id, - Details: _Details, - General: _General, - ItemSummary: _ItemSummary, - StoreItemSummary: _StoreItemSummary, - Mounts: _Mounts, - StoreMounts: _StoreMounts, - Outfits: _Outfits, - StoreOutfits: _StoreOutfits, - Familiars: _Familiars, - Blessings: _Blessings, - Imbuements: _Imbuements, - Charms: _Charms, - CompletedCyclopediaMapAreas: _CompletedCyclopediaMapAreas, - CompletedQuestLines: _CompletedQuestLines, - Titles: _Titles, - Achievements: _Achievements, - BestiaryProgress: _BestiaryProgress, + Details: details, + General: general, + ItemSummary: itemSummary, + StoreItemSummary: storeItemSummary, + Mounts: mounts, + StoreMounts: storeMounts, + Outfits: outfits, + StoreOutfits: storeOutfits, + Familiars: familiars, + Blessings: blessings, + Imbuements: imbuements, + Charms: charms, + CompletedCyclopediaMapAreas: completedCyclopediaMapAreas, + CompletedQuestLines: completedQuestLines, + Titles: titles, + Achievements: achievements, + BestiaryProgress: bestiaryProgress, }, Information: Information{ APIVersion: TibiadataAPIversion, @@ -609,7 +585,7 @@ func ParseItems(s *goquery.Selection) map[string]int { func ParseOutfits(s *goquery.Selection) map[string][]bool { m := make(map[string][]bool) - s.Find(".CVIcon").Each(func(index int, s *goquery.Selection) { + s.Find(CVIconSelector).Each(func(index int, s *goquery.Selection) { outfitTitle, exists := s.Attr("title") if exists { outfitName := strings.Split(outfitTitle, " (")[0] @@ -623,7 +599,7 @@ func ParseOutfits(s *goquery.Selection) map[string][]bool { func ParseMounts(s *goquery.Selection) []string { var mountsList []string - s.Find(".CVIcon").Each(func(index int, s *goquery.Selection) { + s.Find(CVIconSelector).Each(func(index int, s *goquery.Selection) { mountTitle, exists := s.Attr("title") if exists { mountsList = append(mountsList, mountTitle) @@ -632,7 +608,11 @@ func ParseMounts(s *goquery.Selection) []string { return mountsList } -func AjaxJSONDataCollectorV3(TibiaURL string) string { +func AjaxJSONDataCollectorV3(AuctionId int, SectionType int, PageIndex int) string { + TibiaURL := "https://www.tibia.com/websiteservices/handle_charactertrades.php?" + + "auctionid=" + strconv.Itoa(AuctionId) + + "&type=" + strconv.Itoa(SectionType) + + "¤tpage=" + strconv.Itoa(PageIndex) // Setting up resty client client := resty.New() @@ -683,3 +663,25 @@ func AjaxJSONDataCollectorV3(TibiaURL string) string { // Return of extracted html to functions return result.AjaxObjects[0].Data } + +// AjaxResponseObject - child of AjaxJSONData +type AjaxResponseObject struct { + Data string `json:"Data"` + DataType string `json:"DataType"` + Target string `json:"Target"` +} + +// AjaxJSONData - base response for auction items page links +type AjaxJSONData struct { + AjaxObjects []AjaxResponseObject `json:"AjaxObjects"` +} + +const OddEvenSelector = ".Odd,.Even" +const PageLinkSelector = ".PageLink" +const CVIconSelector = ".CVIcon" +const ItemSummarySection = 0 +const StoreItemSummarySection = 1 +const MountsSection = 2 +const StoreMountsSection = 3 +const OutfitsSection = 4 +const StoreOutfitsSection = 5 From be534bca3a1002cc267ea2250ce9aaf426f92581 Mon Sep 17 00:00:00 2001 From: Tobias Lindberg Date: Wed, 19 Jan 2022 22:40:15 +0100 Subject: [PATCH 3/3] rebasing and rewriting a couple of things --- ...arAuctionV3.go => TibiaBazaarAuctionV3.go} | 322 +++++++++--------- src/webserver.go | 33 +- 2 files changed, 193 insertions(+), 162 deletions(-) rename src/{TibiaCharbazaarAuctionV3.go => TibiaBazaarAuctionV3.go} (71%) diff --git a/src/TibiaCharbazaarAuctionV3.go b/src/TibiaBazaarAuctionV3.go similarity index 71% rename from src/TibiaCharbazaarAuctionV3.go rename to src/TibiaBazaarAuctionV3.go index b6b931c7..227ff332 100644 --- a/src/TibiaCharbazaarAuctionV3.go +++ b/src/TibiaBazaarAuctionV3.go @@ -2,146 +2,152 @@ package main import ( "encoding/json" - "github.com/go-resty/resty/v2" "log" "strconv" "strings" "time" + "github.com/go-resty/resty/v2" + "github.com/PuerkitoBio/goquery" - "github.com/gin-gonic/gin" ) -// TibiaCharbazaarAuctionV3 func -func TibiaCharbazaarAuctionV3(c *gin.Context) { - - id := TibiadataStringToIntegerV3(c.Param("id")) +// Child of BazaarAuction +type BazaarAuctionBid struct { + Type string `json:"type"` + Amount int `json:"amount"` +} - // Child of Details - type Bid struct { - Type string `json:"type"` - Amount int `json:"amount"` - } +// Child of BazaarAuction +type BazaarAuctionDetails struct { + CharacterName string `json:"character_name"` + Level int `json:"level"` + Vocation string `json:"vocation"` + Gender string `json:"gender"` + World string `json:"world"` + AuctionStart string `json:"auction_start"` + AuctionEnd string `json:"auction_end"` + Bid BazaarAuctionBid `json:"bid"` +} - // Child of Auction - type Details struct { - CharacterName string `json:"characterName"` - Level int `json:"level"` - Vocation string `json:"vocation"` - Gender string `json:"gender"` - World string `json:"world"` - AuctionStart string `json:"auctionStart"` - AuctionEnd string `json:"auctionEnd"` - Bid Bid `json:"bid"` - } +// Child of BazaarAuction +type General struct { + HitPoints int `json:"hitPoints"` + Mana int `json:"mana"` + Capacity int `json:"capacity"` + Speed int `json:"speed"` + Blessings int `json:"blessings"` + Mounts int `json:"mounts"` + Outfits int `json:"outfits"` + Titles int `json:"titles"` + AxeFighting int `json:"axe_fighting"` + ClubFighting int `json:"club_fighting"` + DistanceFighting int `json:"distance_fighting"` + Fishing int `json:"fishing"` + FistFighting int `json:"fist_fighting"` + MagicLevel int `json:"magic_level"` + Shielding int `json:"shielding"` + SwordFighting int `json:"sword_fighting"` + CreationDate string `json:"creation_date"` + Experience int `json:"experience"` + Gold int `json:"gold"` + AchievementPoints int `json:"achievement_points"` + RegularWorldTransfer string `json:"regular_world_transfer"` // woot + CharmExpansion bool `json:"charm_expansion"` + AvailableCharmPoints int `json:"available_charm_points"` + SpentCharmPoints int `json:"spent_charm_points"` + DailyRewardStreak int `json:"daily_reward_streak"` + HuntingTaskPoints int `json:"hunting_task_points"` + PermanentHuntingTaskSlots int `json:"permanent_hunting_task_slots"` + PermanentPreySlots int `json:"permanent_prey_slots"` + PreyWildCards int `json:"prey_wild_cards"` + Hirelings int `json:"hirelings"` + HirelingJobs int `json:"hireling_jobs"` + HirelingOutfits int `json:"hireling_outfits"` + ExaltedDust int `json:"exalted_dust"` +} - // Child of Auction - type General struct { - HitPoints int `json:"hitPoints"` - Mana int `json:"mana"` - Capacity int `json:"capacity"` - Speed int `json:"speed"` - Blessings int `json:"blessings"` - Mounts int `json:"mounts"` - Outfits int `json:"outfits"` - Titles int `json:"titles"` - AxeFighting int `json:"axeFighting"` - ClubFighting int `json:"clubFighting"` - DistanceFighting int `json:"distanceFighting"` - Fishing int `json:"fishing"` - FistFighting int `json:"fistFighting"` - MagicLevel int `json:"magicLevel"` - Shielding int `json:"shielding"` - SwordFighting int `json:"swordFighting"` - CreationDate string `json:"creationDate"` - Experience int `json:"experience"` - Gold int `json:"gold"` - AchievementPoints int `json:"achievementPoints"` - RegularWorldTransfer string `json:"regularWorldTransfer"` - CharmExpansion bool `json:"charmExpansion"` - AvailableCharmPoints int `json:"availableCharmPoints"` - SpentCharmPoints int `json:"spentCharmPoints"` - DailyRewardStreak int `json:"dailyRewardStreak"` - HuntingTaskPoints int `json:"huntingTaskPoints"` - PermanentHuntingTaskSlots int `json:"permanentHuntingTaskSlots"` - PermanentPreySlots int `json:"permanentPreySlots"` - PreyWildCards int `json:"preyWildCards"` - Hirelings int `json:"hirelings"` - HirelingJobs int `json:"hirelingJobs"` - HirelingOutfits int `json:"hirelingOutfits"` - ExaltedDust int `json:"exaltedDust"` - } +// Child of BazaarAuction +type BazaarAuctionItem struct { + Name string `json:"name"` + Amount int `json:"amount"` +} - // Child of Auction - type Item struct { - Name string `json:"name"` - Amount int `json:"amount"` - } +// Child of BazaarAuction +type BazaarAuctionOutfit struct { + Name string `json:"name"` + Addon1 bool `json:"addon_1"` + Addon2 bool `json:"addon_2"` +} - // Child of Auction - type Outfit struct { - Name string `json:"name"` - Addon1 bool `json:"addon1"` - Addon2 bool `json:"addon2"` - } +// Child of BazaarAuction +type BazaarAuctionBlessings struct { + AdventurersBlessing int `json:"adventurers_blessing"` + BloodOfTheMountain int `json:"blood_of_the_mountain"` + EmbraceOfTibia int `json:"embrace_of_tibia"` + FireOfTheSuns int `json:"fire_of_the_suns"` + HeartOfTheMountain int `json:"heart_of_the_mountain"` + SparkOfThePhoenix int `json:"spark_of_the_phoenix"` + SpiritualShielding int `json:"spiritual_shielding"` + TwistOfFate int `json:"twist_of_fate"` + WisdomOfSolitude int `json:"wisdom_of_solitude"` +} - // Child of Auction - type Blessings struct { - AdventurersBlessing int `json:"adventurersBlessing"` - BloodOfTheMountain int `json:"bloodOfTheMountain"` - EmbraceOfTibia int `json:"embraceOfTibia"` - FireOfTheSuns int `json:"fireOfTheSuns"` - HeartOfTheMountain int `json:"heartOfTheMountain"` - SparkOfThePhoenix int `json:"sparkOfThePhoenix"` - SpiritualShielding int `json:"spiritualShielding"` - TwistOfFate int `json:"twistOfFate"` - WisdomOfSolitude int `json:"wisdomOfSolitude"` - } +// Child of BazaarAuction +type BazaarAuctionCharm struct { + Name string `json:"name"` + Cost int `json:"cost"` +} - // Child of Auction - type Charm struct { - Name string `json:"name"` - Cost int `json:"cost"` - } +// Child of BazaarAuction +type BazaarAuctionBestiaryEntry struct { + Name string `json:"name"` + Kills int `json:"kills"` + Step int `json:"step"` +} - // Child of Auction - type BestiaryEntry struct { - Name string `json:"name"` - Kills int `json:"kills"` - Step int `json:"step"` - } +// Child of BazaarAuctionResponse +type BazaarAuction struct { + Id int `json:"id"` + Details BazaarAuctionDetails `json:"details"` + General General `json:"general"` + ItemSummary []BazaarAuctionItem `json:"item_summary"` + StoreItemSummary []BazaarAuctionItem `json:"store_item_summary"` + Mounts []string `json:"mounts"` + StoreMounts []string `json:"store_mounts"` + Outfits []BazaarAuctionOutfit `json:"outfits"` + StoreOutfits []BazaarAuctionOutfit `json:"store_outfits"` + Familiars []string `json:"familiars"` + Blessings BazaarAuctionBlessings `json:"blessings"` + Imbuements []string `json:"imbuements"` + Charms []BazaarAuctionCharm `json:"charms"` + CompletedCyclopediaMapAreas []string `json:"completed_cyclopedia_map_areas"` + CompletedQuestLines []string `json:"completed_quest_lines"` + Titles []string `json:"titles"` + Achievements []string `json:"achievements"` + BestiaryProgress []BazaarAuctionBestiaryEntry `json:"bestiary_progress"` +} - // Child of JSONData - type Auction struct { - Id int `json:"id"` - Details Details `json:"details"` - General General `json:"general"` - ItemSummary []Item `json:"itemSummary"` - StoreItemSummary []Item `json:"storeItemSummary"` - Mounts []string `json:"mounts"` - StoreMounts []string `json:"storeMounts"` - Outfits []Outfit `json:"outfits"` - StoreOutfits []Outfit `json:"storeOutfits"` - Familiars []string `json:"familiars"` - Blessings Blessings `json:"blessings"` - Imbuements []string `json:"imbuements"` - Charms []Charm `json:"charms"` - CompletedCyclopediaMapAreas []string `json:"completedCyclopediaMapAreas"` - CompletedQuestLines []string `json:"completedQuestLines"` - Titles []string `json:"titles"` - Achievements []string `json:"achievements"` - BestiaryProgress []BestiaryEntry `json:"bestiaryProgress"` - } +// The base includes two levels: Auction and Information +type BazaarAuctionResponse struct { + Auction BazaarAuction `json:"auction"` + Information Information `json:"information"` +} - // The base includes two levels: Auction and Information - type JSONData struct { - Auction Auction `json:"auction"` - Information Information `json:"information"` - } +const ( + OddEvenSelector = ".Odd,.Even" + PageLinkSelector = ".PageLink" + CVIconSelector = ".CVIcon" + ItemSummarySection = 0 + StoreItemSummarySection = 1 + MountsSection = 2 + StoreMountsSection = 3 + OutfitsSection = 4 + StoreOutfitsSection = 5 +) - // Getting data with TibiadataHTMLDataCollectorV3 - BoxContentHTML := TibiadataHTMLDataCollectorV3("https://www.tibia.com/charactertrade/?page=details&auctionid=" + - strconv.Itoa(id)) +// TibiaBazaarAuctionV3Impl func +func TibiaBazaarAuctionV3Impl(BoxContentHTML string) BazaarAuctionResponse { // Loading HTML data into ReaderHTML for goquery with NewReader ReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(BoxContentHTML)) @@ -149,15 +155,21 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { log.Fatal(err) } + var id int + ReaderHTML.Find("input[name=auctionid]").Each(func(i int, selection *goquery.Selection) { + // collect the auction ID + id = TibiadataStringToIntegerV3(selection.AttrOr("value", "")) + }) + // Extract details section - var details Details + var details BazaarAuctionDetails ReaderHTML.Find(".Auction").Each(func(index int, s *goquery.Selection) { detailsHeader := strings.Split(s.Find(".AuctionHeader").Text(), "Level: ") details.CharacterName = detailsHeader[0] detailsHeader = strings.Split(detailsHeader[1], "|") - level := TibiadataStringToIntegerV3(detailsHeader[0]) + level := TibiadataStringToIntegerV3(strings.TrimSpace(detailsHeader[0])) details.Level = level details.Vocation = strings.TrimSpace(strings.Split(detailsHeader[1], "Vocation: ")[1]) @@ -177,9 +189,11 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { auctionStartDate := TibiaDataSanitizeNbspSpaceString(nodes[1+lookupIndex].FirstChild.Data) auctionStartDate = strings.Split(auctionStartDate, " CET")[0] + ":00 CET" + auctionStartDate = strings.Split(auctionStartDate, " CEST")[0] + ":00 CEST" auctionEndDate := TibiaDataSanitizeNbspSpaceString(nodes[3+lookupIndex].FirstChild.Data) auctionEndDate = strings.Split(auctionEndDate, " CET")[0] + ":00 CET" + auctionEndDate = strings.Split(auctionEndDate, " CEST")[0] + ":00 CEST" details.AuctionStart = TibiadataDatetimeV3(auctionStartDate) details.AuctionEnd = TibiadataDatetimeV3(auctionEndDate) @@ -187,7 +201,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { bidType := strings.Split(nodes[4+lookupIndex].FirstChild.FirstChild.Data, " Bid:")[0] bidAmount := TibiadataStringToIntegerV3(nodes[4+lookupIndex].LastChild.FirstChild.FirstChild.Data) - details.Bid = Bid{ + details.Bid = BazaarAuctionBid{ Type: bidType, Amount: bidAmount, } @@ -247,11 +261,11 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract items summary - var itemSummary []Item + var itemSummary []BazaarAuctionItem ReaderHTML.Find("#ItemSummary").Each(func(index int, s *goquery.Selection) { for k, v := range ParseItems(s) { - itemSummary = append(itemSummary, Item{Name: k, Amount: v}) + itemSummary = append(itemSummary, BazaarAuctionItem{Name: k, Amount: v}) } totalPages := s.Find(PageLinkSelector).Size() @@ -264,18 +278,18 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { log.Fatal(err) } for k, v := range ParseItems(ItemPageReaderHTML.Contents()) { - itemSummary = append(itemSummary, Item{Name: k, Amount: v}) + itemSummary = append(itemSummary, BazaarAuctionItem{Name: k, Amount: v}) } } } }) // Extract store items summary - var storeItemSummary []Item + var storeItemSummary []BazaarAuctionItem ReaderHTML.Find("#StoreItemSummary").Each(func(index int, s *goquery.Selection) { for k, v := range ParseItems(s) { - storeItemSummary = append(storeItemSummary, Item{Name: k, Amount: v}) + storeItemSummary = append(storeItemSummary, BazaarAuctionItem{Name: k, Amount: v}) } totalPages := s.Find(PageLinkSelector).Size() @@ -288,7 +302,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { log.Fatal(err) } for k, v := range ParseItems(ItemPageReaderHTML.Contents()) { - storeItemSummary = append(storeItemSummary, Item{Name: k, Amount: v}) + storeItemSummary = append(storeItemSummary, BazaarAuctionItem{Name: k, Amount: v}) } } } @@ -335,10 +349,10 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract outfits - var outfits []Outfit + var outfits []BazaarAuctionOutfit ReaderHTML.Find("#Outfits").Each(func(index int, s *goquery.Selection) { for k, v := range ParseOutfits(s) { - outfits = append(outfits, Outfit{ + outfits = append(outfits, BazaarAuctionOutfit{ Name: k, Addon1: v[0], Addon2: v[1], @@ -354,7 +368,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { log.Fatal(err) } for k, v := range ParseOutfits(OutfitsPageReaderHTML.Contents()) { - outfits = append(outfits, Outfit{ + outfits = append(outfits, BazaarAuctionOutfit{ Name: k, Addon1: v[0], Addon2: v[1], @@ -365,10 +379,10 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract store outfits - var storeOutfits []Outfit + var storeOutfits []BazaarAuctionOutfit ReaderHTML.Find("#StoreOutfits").Each(func(index int, s *goquery.Selection) { for k, v := range ParseOutfits(s) { - outfits = append(outfits, Outfit{ + outfits = append(outfits, BazaarAuctionOutfit{ Name: k, Addon1: v[0], Addon2: v[1], @@ -384,7 +398,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { log.Fatal(err) } for k, v := range ParseOutfits(OutfitsPageReaderHTML.Contents()) { - outfits = append(outfits, Outfit{ + outfits = append(outfits, BazaarAuctionOutfit{ Name: k, Addon1: v[0], Addon2: v[1], @@ -406,7 +420,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract blessings - var blessings Blessings + var blessings BazaarAuctionBlessings ReaderHTML.Find("#Blessings").Each(func(index int, s *goquery.Selection) { s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild @@ -449,7 +463,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract Charms - var charms []Charm + var charms []BazaarAuctionCharm ReaderHTML.Find("#Charms").Each(func(index int, s *goquery.Selection) { s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild @@ -457,7 +471,7 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { return } if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - charms = append(charms, Charm{ + charms = append(charms, BazaarAuctionCharm{ Cost: TibiadataStringToIntegerV3(node.FirstChild.Data), Name: node.NextSibling.FirstChild.Data, }) @@ -513,12 +527,12 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) // Extract Bestiary Progress - var bestiaryProgress []BestiaryEntry + var bestiaryProgress []BazaarAuctionBestiaryEntry ReaderHTML.Find("#BestiaryProgress").Each(func(index int, s *goquery.Selection) { s.Find(OddEvenSelector).Each(func(index int, s *goquery.Selection) { node := s.Nodes[0].FirstChild if !strings.Contains(node.Parent.Attr[0].Val, "IndicateMoreEntries") { - bestiaryProgress = append(bestiaryProgress, BestiaryEntry{ + bestiaryProgress = append(bestiaryProgress, BazaarAuctionBestiaryEntry{ Step: TibiadataStringToIntegerV3(node.FirstChild.Data), Kills: TibiadataStringToIntegerV3(strings.Split(node.NextSibling.FirstChild.Data, " x")[0]), Name: node.NextSibling.NextSibling.FirstChild.Data, @@ -527,8 +541,9 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { }) }) - jsonData := JSONData{ - Auction: Auction{ + // Build the data-blob + return BazaarAuctionResponse{ + BazaarAuction{ Id: id, Details: details, General: general, @@ -548,14 +563,11 @@ func TibiaCharbazaarAuctionV3(c *gin.Context) { Achievements: achievements, BestiaryProgress: bestiaryProgress, }, - Information: Information{ + Information{ APIVersion: TibiadataAPIversion, Timestamp: TibiadataDatetimeV3(""), }, } - - // return jsonData - TibiaDataAPIHandleSuccessResponse(c, "TibiaCharbazaarAuctionV3", jsonData) } func ParseItems(s *goquery.Selection) map[string]int { @@ -565,8 +577,10 @@ func ParseItems(s *goquery.Selection) map[string]int { itemTitle, exists := s.Attr("title") if exists { - var itemAmount int - var itemName string + var ( + itemAmount int + itemName string + ) nodes := s.Find(".ObjectAmount").First().Nodes if nodes == nil { @@ -675,13 +689,3 @@ type AjaxResponseObject struct { type AjaxJSONData struct { AjaxObjects []AjaxResponseObject `json:"AjaxObjects"` } - -const OddEvenSelector = ".Odd,.Even" -const PageLinkSelector = ".PageLink" -const CVIconSelector = ".CVIcon" -const ItemSummarySection = 0 -const StoreItemSummarySection = 1 -const MountsSection = 2 -const StoreMountsSection = 3 -const OutfitsSection = 4 -const StoreOutfitsSection = 5 diff --git a/src/webserver.go b/src/webserver.go index faf00e5d..adc5572d 100644 --- a/src/webserver.go +++ b/src/webserver.go @@ -71,6 +71,9 @@ func runWebServer() { // TibiaData API version 3 v3 := router.Group("/v3") { + // Tibia bazaar + v3.GET("/bazaar/auction/:id", tibiaBazaarAuctionV3) + // Tibia characters v3.GET("/characters/character/:character", tibiaCharactersCharacterV3) @@ -118,9 +121,6 @@ func runWebServer() { // Tibia worlds v3.GET("/worlds", tibiaWorldsOverviewV3) v3.GET("/worlds/world/:world", tibiaWorldsWorldV3) - - // TibiaBazaarAuctionV3 - v3.GET("/charbazaar/auction/:id", TibiaCharbazaarAuctionV3) } // container version details endpoint @@ -137,6 +137,33 @@ func runWebServer() { _ = router.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") } +// Bazaar auction godoc +// @Summary Show one bazaar auction +// @Description Show all information about one bazaar auction +// @Tags bazaar +// @Accept json +// @Produce json +// @Param id path int true "The ID of auction" +// @Success 200 {object} BazaarAuctionResponse +// @Router /v3/bazaar/auction/{id} [get] +func tibiaBazaarAuctionV3(c *gin.Context) { + // getting params from URL + id := c.Param("id") + + tibiadataRequest := TibiadataRequestStruct{ + Method: resty.MethodGet, + URL: "https://www.tibia.com/charactertrade/?page=details&auctionid=" + id, + } + + tibiaDataRequestHandler( + c, + tibiadataRequest, + func(BoxContentHTML string) (interface{}, int) { + return TibiaBazaarAuctionV3Impl(BoxContentHTML), http.StatusOK + }, + "TibiaBazaarAuctionV3") +} + // Character godoc // @Summary Show one character // @Description Show all information about one character available