Skip to content

Commit 37eeae4

Browse files
rewrite boostable bosses parser (#233)
Co-authored-by: Tobias Lindberg <tobias.ehlert@gmail.com>
1 parent aa88f3f commit 37eeae4

File tree

5 files changed

+213
-125
lines changed

5 files changed

+213
-125
lines changed

src/TibiaBoostableBossesOverview.go

+96-66
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package main
22

33
import (
4-
"fmt"
4+
"errors"
55
"net/http"
6-
"regexp"
76
"strings"
87

9-
"github.com/PuerkitoBio/goquery"
8+
"github.com/TibiaData/tibiadata-api-go/src/validation"
109
)
1110

1211
// Child of BoostableBoss (used for list of boostable bosses and boosted boss section)
@@ -28,95 +27,126 @@ type BoostableBossesOverviewResponse struct {
2827
Information Information `json:"information"`
2928
}
3029

31-
var (
32-
BoostedBossNameRegex = regexp.MustCompile(`<b>(.*)</b>`)
33-
BoostedBossImageRegex = regexp.MustCompile(`<img[^>]+\bsrc=["']([^"']+)["']`)
34-
BoostableBossInformationRegex = regexp.MustCompile(`<img src="(.*)" border.*div>(.*)<\/div>`)
35-
)
30+
func TibiaBoostableBossesOverviewImpl(BoxContentHTML string) (BoostableBossesOverviewResponse, error) {
31+
const (
32+
bodyIndexer = `<body`
33+
endBodyIndexer = `</body>`
3634

37-
func TibiaBoostableBossesOverviewImpl(BoxContentHTML string) (*BoostableBossesOverviewResponse, error) {
38-
// Creating empty vars
39-
var (
40-
BoostedBossName, BoostedBossImage string
35+
todayChecker = `Today's boosted boss: `
36+
bossesChecker = `<div class="CaptionContainer">`
37+
38+
todayBossIndexer = `title="` + todayChecker
39+
endTodayBossIndexer = `" src="`
40+
41+
todayBossImgIndexer = `https://static.tibia.com/images/global/header/monsters/`
42+
endTodayBossImgIndexer = `" onClick="`
43+
44+
bossesImgIndexer = `https://static.tibia.com/images/library/`
45+
endBossesImgIndexer = `"`
46+
47+
bossesNameIndexer = `border=0 /> <div>`
48+
endBossesNameIndexer = `</div>`
49+
)
50+
51+
bodyIdx := strings.Index(
52+
BoxContentHTML, bodyIndexer,
4153
)
42-
// Loading HTML data into ReaderHTML for goquery with NewReader
43-
ReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(BoxContentHTML))
44-
if err != nil {
45-
return nil, fmt.Errorf("[error] TibiaBoostableBossesOverviewImpl failed at goquery.NewDocumentFromReader, err: %s", err)
46-
}
4754

48-
// Getting data from div.InnerTableContainer and then first p
49-
InnerTableContainerTMPB, err := ReaderHTML.Find(".InnerTableContainer p").First().Html()
50-
if err != nil {
51-
return nil, fmt.Errorf("[error] TibiaBoostableBossesOverviewImpl failed at ReaderHTML.Find, error: %s", err)
55+
if bodyIdx == -1 {
56+
return BoostableBossesOverviewResponse{}, errors.New("[error] body passd to TibiaBoostableBossesOverviewImpl is not valid")
5257
}
5358

54-
// Regex to get data for name for boosted boss
55-
subma1b := BoostedBossNameRegex.FindAllStringSubmatch(InnerTableContainerTMPB, -1)
59+
endBodyIdx := strings.Index(
60+
BoxContentHTML[bodyIdx:], endBodyIndexer,
61+
) + bodyIdx + len(endBodyIndexer)
5662

57-
if len(subma1b) > 0 {
58-
// Settings vars for usage in JSONData
59-
BoostedBossName = subma1b[0][1]
63+
if endBodyIdx == -1 {
64+
return BoostableBossesOverviewResponse{}, errors.New("[error] body passd to TibiaBoostableBossesOverviewImpl is not valid")
6065
}
6166

62-
// Regex to get image of boosted boss
63-
subma2b := BoostedBossImageRegex.FindAllStringSubmatch(InnerTableContainerTMPB, -1)
67+
data := BoxContentHTML[bodyIdx:endBodyIdx]
6468

65-
if len(subma2b) > 0 {
66-
// Settings vars for usage in JSONData
67-
BoostedBossImage = subma2b[0][1]
68-
}
69+
var (
70+
started bool
6971

70-
// Creating empty BoostableBossesData var
71-
var BoostableBossesData []OverviewBoostableBoss
72+
boostedBossName string
73+
boostedBossImg string
7274

73-
var insideError error
75+
bosses = make([]OverviewBoostableBoss, 0, validation.AmountOfBoostableBosses)
76+
)
7477

75-
// Running query over each div
76-
ReaderHTML.Find(".BoxContent div div").EachWithBreak(func(index int, s *goquery.Selection) bool {
78+
split := strings.Split(data, "\n")
79+
for _, cur := range split {
80+
isTodaysLine := strings.Contains(cur, todayChecker) && !started
81+
isBossesLine := strings.Contains(cur, bossesChecker)
7782

78-
// Storing HTML into BoostableBossDivHTML
79-
BoostableBossDivHTML, err := s.Html()
80-
if err != nil {
81-
insideError = fmt.Errorf("[error] TibiaBoostableBossesOverviewImpl failed at BoostableBossDivHTML, err := s.Html(), err: %s", err)
82-
return false
83+
if !isTodaysLine && !isBossesLine {
84+
continue
8385
}
8486

85-
// Regex to get data for name, race and img src param for creature
86-
subma1 := BoostableBossInformationRegex.FindAllStringSubmatch(BoostableBossDivHTML, -1)
87+
if isTodaysLine {
88+
started = true
8789

88-
// check if regex return length is over 0 and the match of name is over 1
89-
if len(subma1) > 0 && len(subma1[0][2]) > 1 {
90-
// Adding bool to indicate features in boostable_boss_list
91-
FeaturedRace := false
92-
if subma1[0][2] == BoostedBossName {
93-
FeaturedRace = true
94-
}
90+
todayBossIdx := strings.Index(
91+
cur, todayBossIndexer,
92+
) + len(todayBossIndexer)
93+
endTodayBossIdx := strings.Index(
94+
cur[todayBossIdx:], endTodayBossIndexer,
95+
) + todayBossIdx
9596

96-
// Creating data block to return
97-
BoostableBossesData = append(BoostableBossesData, OverviewBoostableBoss{
98-
Name: TibiaDataSanitizeEscapedString(subma1[0][2]),
99-
ImageURL: subma1[0][1],
100-
Featured: FeaturedRace,
101-
})
97+
boostedBossName = TibiaDataSanitizeEscapedString(
98+
cur[todayBossIdx:endTodayBossIdx],
99+
)
100+
101+
todayBossImgIdx := strings.Index(
102+
cur[todayBossIdx:], todayBossImgIndexer,
103+
) + todayBossIdx
104+
endTodayBossImgIdx := strings.Index(
105+
cur[todayBossImgIdx:], endTodayBossImgIndexer,
106+
) + todayBossImgIdx
107+
108+
boostedBossImg = cur[todayBossImgIdx:endTodayBossImgIdx]
102109
}
103110

104-
return true
105-
})
111+
if isBossesLine {
112+
for idx := strings.Index(cur, bossesImgIndexer); idx != -1; idx = strings.Index(cur, bossesImgIndexer) {
113+
imgIdx := strings.Index(
114+
cur, bossesImgIndexer,
115+
)
116+
endImgIdx := strings.Index(
117+
cur[imgIdx:], endBossesImgIndexer,
118+
) + imgIdx
119+
img := cur[imgIdx:endImgIdx]
120+
121+
nameIdx := strings.Index(
122+
cur, bossesNameIndexer,
123+
) + len(bossesNameIndexer)
124+
endNameIdx := strings.Index(
125+
cur[nameIdx:], endBossesNameIndexer,
126+
) + nameIdx
127+
name := TibiaDataSanitizeEscapedString(cur[nameIdx:endNameIdx])
128+
129+
bosses = append(bosses, OverviewBoostableBoss{
130+
Name: name,
131+
ImageURL: img,
132+
Featured: name == boostedBossName,
133+
})
134+
135+
cur = cur[endNameIdx-1:]
136+
}
106137

107-
if insideError != nil {
108-
return nil, insideError
138+
break
139+
}
109140
}
110141

111-
// Build the data-blob
112-
return &BoostableBossesOverviewResponse{
142+
return BoostableBossesOverviewResponse{
113143
BoostableBossesContainer{
114144
Boosted: OverviewBoostableBoss{
115-
Name: TibiaDataSanitizeEscapedString(BoostedBossName),
116-
ImageURL: BoostedBossImage,
145+
Name: boostedBossName,
146+
ImageURL: boostedBossImg,
117147
Featured: true,
118148
},
119-
BoostableBosses: BoostableBossesData,
149+
BoostableBosses: bosses,
120150
},
121151
Information{
122152
APIDetails: TibiaDataAPIDetails,

src/TibiaBoostableBossesOverview_test.go

+76-15
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,85 @@ func TestBoostableBossesOverview(t *testing.T) {
2222

2323
boostableBossesJson, _ := TibiaBoostableBossesOverviewImpl(string(data))
2424
assert := assert.New(t)
25+
boosted := boostableBossesJson.BoostableBosses.Boosted
26+
bosses := boostableBossesJson.BoostableBosses.BoostableBosses
2527

26-
assert.Equal("Goshnar's Malice", boostableBossesJson.BoostableBosses.Boosted.Name)
27-
assert.Equal("https://static.tibia.com/images/global/header/monsters/goshnarsmalice.gif", boostableBossesJson.BoostableBosses.Boosted.ImageURL)
28+
assert.Equal(91, len(bosses))
29+
assert.Equal("Sharpclaw", boosted.Name)
30+
assert.Equal(
31+
"https://static.tibia.com/images/global/header/monsters/sharpclaw.gif",
32+
boosted.ImageURL,
33+
)
2834

29-
assert.Equal(89, len(boostableBossesJson.BoostableBosses.BoostableBosses))
35+
for _, tc := range []struct {
36+
idx int
37+
name string
38+
featured bool
39+
imageURL string
40+
}{
41+
{
42+
idx: 19,
43+
name: "Gnomevil",
44+
featured: false,
45+
imageURL: "https://static.tibia.com/images/library/gnomehorticulist.gif",
46+
},
47+
{
48+
idx: 24,
49+
name: "Goshnar's Malice",
50+
featured: false,
51+
imageURL: "https://static.tibia.com/images/library/goshnarsmalice.gif",
52+
},
53+
{
54+
idx: 52,
55+
name: "Sharpclaw",
56+
featured: true,
57+
imageURL: "https://static.tibia.com/images/library/sharpclaw.gif",
58+
},
59+
{
60+
idx: 75,
61+
name: "The Pale Worm",
62+
featured: false,
63+
imageURL: "https://static.tibia.com/images/library/paleworm.gif",
64+
},
65+
} {
66+
boss := bosses[tc.idx]
67+
assert.Equal(
68+
tc.name, boss.Name,
69+
"Wrong name\nidx: %d (%s)\nwant: %s\ngot: %s",
70+
tc.idx, tc.name, tc.name, boss.Name,
71+
)
72+
assert.Equal(
73+
tc.featured, boss.Featured,
74+
"Wrong featured status\nidx: %d (%s)\nwant: %v\ngot: %v",
75+
tc.idx, tc.name, tc.featured, boss.Featured,
76+
)
77+
assert.Equal(
78+
tc.imageURL, boss.ImageURL,
79+
"Wrong image URL\nidx: %d (%s)\nwant: %s\ngot: %s",
80+
tc.idx, tc.name, tc.imageURL, boss.ImageURL,
81+
)
82+
}
83+
}
3084

31-
gnomevil := boostableBossesJson.BoostableBosses.BoostableBosses[18]
32-
assert.Equal("Gnomevil", gnomevil.Name)
33-
assert.Equal("https://static.tibia.com/images/library/gnomehorticulist.gif", gnomevil.ImageURL)
34-
assert.False(gnomevil.Featured)
85+
var bossSink BoostableBossesOverviewResponse
3586

36-
goshnarsmalice := boostableBossesJson.BoostableBosses.BoostableBosses[23]
37-
assert.Equal("Goshnar's Malice", goshnarsmalice.Name)
38-
assert.Equal("https://static.tibia.com/images/library/goshnarsmalice.gif", goshnarsmalice.ImageURL)
39-
assert.True(goshnarsmalice.Featured)
87+
func BenchmarkTibiaBoostableBossesOverviewImpl(b *testing.B) {
88+
file, err := static.TestFiles.Open("testdata/boostablebosses/boostablebosses.html")
89+
if err != nil {
90+
b.Fatalf("file opening error: %s", err)
91+
}
92+
defer file.Close()
4093

41-
paleworm := boostableBossesJson.BoostableBosses.BoostableBosses[73]
42-
assert.Equal("The Pale Worm", paleworm.Name)
43-
assert.Equal("https://static.tibia.com/images/library/paleworm.gif", paleworm.ImageURL)
44-
assert.False(paleworm.Featured)
94+
rawData, err := io.ReadAll(file)
95+
if err != nil {
96+
b.Fatalf("File reading error: %s", err)
97+
}
98+
data := string(rawData)
99+
100+
b.ReportAllocs()
101+
b.ResetTimer()
102+
103+
for i := 0; i < b.N; i++ {
104+
bossSink, _ = TibiaBoostableBossesOverviewImpl(data)
105+
}
45106
}

0 commit comments

Comments
 (0)