Skip to content

Commit 41d8e4b

Browse files
authored
Feature: Tibia News endpoints (#32)
* adding TibiaNewslistV3 & TibiaNewsV3
1 parent 72fc5f1 commit 41d8e4b

File tree

5 files changed

+313
-1
lines changed

5 files changed

+313
-1
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ Those are the current existing endpoints.
107107
- GET `/v3/highscores/world/:world/:category`
108108
- GET `/v3/highscores/world/:world/:category/:vocation`
109109
- GET `/v3/killstatistics/world/:world`
110+
- GET `/v3/news/archive`
111+
- GET `/v3/news/archive/:days`
112+
- GET `/v3/news/id/:news_id`
113+
- GET `/v3/news/latest`
114+
- GET `/v3/news/newsticker`
110115
- GET `/v3/spells`
111116
- GET `/v3/spells/spell/:spell`
112117
- GET `/v3/spells/vocation/:vocation`

src/TibiaDataUtils.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func TibiadataDateV3(date string) string {
111111
func TibiadataStringToIntegerV3(data string) int {
112112
returnData, err := strconv.Atoi(data)
113113
if err != nil {
114-
log.Printf("[warning] TibiadataStringToIntegerV3: couldn't convert %s into an int. error: %s", data, err)
114+
log.Printf("[warning] TibiadataStringToIntegerV3: couldn't convert string into int. error: %s", err)
115115
}
116116

117117
return returnData
@@ -238,3 +238,35 @@ func TibiaDataVocationValidator(vocation string) (string, string) {
238238
// returning vars
239239
return vocation, vocationid
240240
}
241+
242+
// TibiadataGetNewsCategory func - extract news category by newsicon
243+
func TibiadataGetNewsCategory(data string) string {
244+
switch {
245+
case strings.Contains(data, "newsicon_cipsoft"):
246+
return "cipsoft"
247+
case strings.Contains(data, "newsicon_community"):
248+
return "community"
249+
case strings.Contains(data, "newsicon_development"):
250+
return "development"
251+
case strings.Contains(data, "newsicon_support"):
252+
return "support"
253+
case strings.Contains(data, "newsicon_technical"):
254+
return "technical"
255+
default:
256+
return "unknown"
257+
}
258+
}
259+
260+
// TibiadataGetNewsType func - extract news type
261+
func TibiadataGetNewsType(data string) string {
262+
switch data {
263+
case "News Ticker":
264+
return "ticker"
265+
case "Featured Article":
266+
return "article"
267+
case "News":
268+
return "news"
269+
default:
270+
return "unknown"
271+
}
272+
}

src/TibiaNewsV3.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"regexp"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/PuerkitoBio/goquery"
11+
"github.com/gin-gonic/gin"
12+
)
13+
14+
var (
15+
martelRegex = regexp.MustCompile(`<img src=\"https:\/\/static\.tibia\.com\/images\/global\/letters\/letter_martel_(.)\.gif\" ([^\/>]+..)`)
16+
)
17+
18+
// TibiaNewsV3 func
19+
func TibiaNewsV3(c *gin.Context) {
20+
21+
// getting params from URL
22+
NewsID := TibiadataStringToIntegerV3(c.Param("news_id"))
23+
24+
// checking the NewsID provided
25+
if NewsID <= 0 {
26+
TibiaDataAPIHandleOtherResponse(c, http.StatusBadRequest, "TibiaNewsV3", gin.H{"error": "no valid news_id provided"})
27+
return
28+
}
29+
30+
// Child of JSONData
31+
type News struct {
32+
ID int `json:"id"`
33+
Date string `json:"date"`
34+
Title string `json:"title,omitempty"`
35+
Category string `json:"category"`
36+
Type string `json:"type,omitempty"`
37+
TibiaURL string `json:"url"`
38+
Content string `json:"content"`
39+
ContentHTML string `json:"content_html"`
40+
}
41+
42+
//
43+
// The base
44+
type JSONData struct {
45+
News News `json:"news"`
46+
Information Information `json:"information"`
47+
}
48+
49+
// Declaring vars for later use..
50+
var (
51+
NewsData News
52+
tmp1 *goquery.Selection
53+
tmp2 string
54+
)
55+
56+
TibiadataRequest.URL = "https://www.tibia.com/news/?subtopic=newsarchive&id=" + strconv.Itoa(NewsID)
57+
58+
// Getting data with TibiadataHTMLDataCollectorV3
59+
BoxContentHTML, err := TibiadataHTMLDataCollectorV3(TibiadataRequest)
60+
61+
// return error (e.g. for maintenance mode)
62+
if err != nil {
63+
TibiaDataAPIHandleOtherResponse(c, http.StatusBadGateway, "TibiaNewslistV3", gin.H{"error": err.Error()})
64+
return
65+
}
66+
67+
// Loading HTML data into ReaderHTML for goquery with NewReader
68+
ReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(BoxContentHTML))
69+
if err != nil {
70+
log.Fatal(err)
71+
}
72+
73+
NewsData.ID = NewsID
74+
NewsData.TibiaURL = TibiadataRequest.URL
75+
76+
ReaderHTML.Find(".NewsHeadline").Each(func(index int, s *goquery.Selection) {
77+
78+
// getting category by image src
79+
CategoryImg, _ := s.Find("img").Attr("src")
80+
NewsData.Category = TibiadataGetNewsCategory(CategoryImg)
81+
82+
// getting date from headline
83+
tmp1 = s.Find(".NewsHeadlineDate")
84+
tmp2, _ = tmp1.Html()
85+
NewsData.Date = TibiadataDateV3(strings.ReplaceAll(tmp2, " - ", ""))
86+
87+
// getting headline text (which could be title or also type)
88+
tmp1 = s.Find(".NewsHeadlineText")
89+
tmp2, _ = tmp1.Html()
90+
NewsData.Title = RemoveHtmlTag(tmp2)
91+
if NewsData.Title == "News Ticker" {
92+
NewsData.Type = "ticker"
93+
NewsData.Title = ""
94+
}
95+
})
96+
97+
ReaderHTML.Find(".NewsTableContainer").Each(func(index int, s *goquery.Selection) {
98+
99+
// checking if its a ticker..
100+
if NewsData.Type == "ticker" {
101+
tmp1 = s.Find("p")
102+
NewsData.Content = tmp1.Text()
103+
NewsData.ContentHTML, _ = tmp1.Html()
104+
} else {
105+
// getting html
106+
tmp2, _ = s.First().Html()
107+
// replacing martel letter in articles with real letters
108+
tmp2 = martelRegex.ReplaceAllString(tmp2, "$1")
109+
s.ReplaceWithHtml(tmp2)
110+
111+
// storing html content
112+
NewsData.ContentHTML = tmp2
113+
114+
// reading string again after replacing letters
115+
tmp1, _ := goquery.NewDocumentFromReader(strings.NewReader(tmp2))
116+
117+
// storing text content
118+
NewsData.Content = tmp1.Text()
119+
}
120+
})
121+
122+
//
123+
// Build the data-blob
124+
jsonData := JSONData{
125+
NewsData,
126+
Information{
127+
APIVersion: TibiadataAPIversion,
128+
Timestamp: TibiadataDatetimeV3(""),
129+
},
130+
}
131+
132+
TibiaDataAPIHandleSuccessResponse(c, "TibiaNewsV3", jsonData)
133+
}

src/TibiaNewslistV3.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"net/url"
7+
"strconv"
8+
"strings"
9+
"time"
10+
11+
"github.com/PuerkitoBio/goquery"
12+
"github.com/gin-gonic/gin"
13+
)
14+
15+
// TibiaNewslistV3 func
16+
func TibiaNewslistV3(c *gin.Context) {
17+
18+
// getting params from URL
19+
days := TibiadataStringToIntegerV3(c.Param("days"))
20+
if days == 0 {
21+
days = 90 // default for recent posts
22+
}
23+
24+
// Child of JSONData
25+
type News struct {
26+
ID int `json:"id"`
27+
Date string `json:"date"`
28+
News string `json:"news"`
29+
Category string `json:"category"`
30+
Type string `json:"type"`
31+
TibiaURL string `json:"url"`
32+
ApiURL string `json:"url_api,omitempty"`
33+
}
34+
35+
//
36+
// The base
37+
type JSONData struct {
38+
News []News `json:"news"`
39+
Information Information `json:"information"`
40+
}
41+
42+
// Declaring vars for later use..
43+
var NewsListData []News
44+
45+
// generating dates to pass to FormData
46+
DateBegin := time.Now().AddDate(0, 0, -days)
47+
DateEnd := time.Now()
48+
49+
TibiadataRequest.Method = http.MethodPost
50+
TibiadataRequest.URL = "https://www.tibia.com/news/?subtopic=newsarchive"
51+
TibiadataRequest.FormData = map[string]string{
52+
"filter_begin_day": strconv.Itoa(DateBegin.UTC().Day()), // period
53+
"filter_begin_month": strconv.Itoa(int(DateBegin.UTC().Month())), // period
54+
"filter_begin_year": strconv.Itoa(DateBegin.UTC().Year()), // period
55+
"filter_end_day": strconv.Itoa(DateEnd.UTC().Day()), // period
56+
"filter_end_month": strconv.Itoa(int(DateEnd.UTC().Month())), // period
57+
"filter_end_year": strconv.Itoa(DateEnd.UTC().Year()), // period
58+
"filter_cipsoft": "cipsoft", // category
59+
"filter_community": "community", // category
60+
"filter_development": "development", // category
61+
"filter_support": "support", // category
62+
"filter_technical": "technical", // category
63+
}
64+
65+
// getting type of news list
66+
switch tmp := strings.Split(c.Request.URL.Path, "/"); tmp[3] {
67+
case "newsticker":
68+
TibiadataRequest.FormData["filter_ticker"] = "ticker"
69+
case "latest":
70+
TibiadataRequest.FormData["filter_article"] = "article"
71+
TibiadataRequest.FormData["filter_news"] = "news"
72+
case "archive":
73+
TibiadataRequest.FormData["filter_ticker"] = "ticker"
74+
TibiadataRequest.FormData["filter_article"] = "article"
75+
TibiadataRequest.FormData["filter_news"] = "news"
76+
}
77+
78+
// Getting data with TibiadataHTMLDataCollectorV3
79+
BoxContentHTML, err := TibiadataHTMLDataCollectorV3(TibiadataRequest)
80+
81+
// return error (e.g. for maintenance mode)
82+
if err != nil {
83+
TibiaDataAPIHandleOtherResponse(c, http.StatusBadGateway, "TibiaNewslistV3", gin.H{"error": err.Error()})
84+
return
85+
}
86+
87+
// Loading HTML data into ReaderHTML for goquery with NewReader
88+
ReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(BoxContentHTML))
89+
if err != nil {
90+
log.Fatal(err)
91+
}
92+
93+
ReaderHTML.Find(".Odd,.Even").Each(func(index int, s *goquery.Selection) {
94+
var OneNews News
95+
96+
// getting category by image src
97+
CategoryImg, _ := s.Find("img").Attr("src")
98+
OneNews.Category = TibiadataGetNewsCategory(CategoryImg)
99+
100+
// getting type from headline
101+
NewsType := s.Nodes[0].FirstChild.NextSibling.FirstChild.NextSibling.NextSibling.FirstChild.Data
102+
OneNews.Type = TibiadataGetNewsType(TibiaDataSanitizeNbspSpaceString(NewsType))
103+
104+
// getting date from headline
105+
OneNews.Date = TibiadataDateV3(s.Nodes[0].FirstChild.NextSibling.FirstChild.Data)
106+
OneNews.News = s.Find("a").Text()
107+
108+
// getting remaining things as URLs
109+
NewsURL, _ := s.Find("a").Attr("href")
110+
p, _ := url.Parse(NewsURL)
111+
NewsID := p.Query().Get("id")
112+
NewsSplit := strings.Split(NewsURL, NewsID)
113+
OneNews.ID = TibiadataStringToIntegerV3(NewsID)
114+
OneNews.TibiaURL = NewsSplit[0] + NewsID
115+
116+
if TibiadataHost != "" {
117+
OneNews.ApiURL = "http://api.tibiadata.com/v3/news/id/" + NewsID
118+
}
119+
120+
// add to NewsListData for response
121+
NewsListData = append(NewsListData, OneNews)
122+
})
123+
124+
//
125+
// Build the data-blob
126+
jsonData := JSONData{
127+
NewsListData,
128+
Information{
129+
APIVersion: TibiadataAPIversion,
130+
Timestamp: TibiadataDatetimeV3(""),
131+
},
132+
}
133+
134+
TibiaDataAPIHandleSuccessResponse(c, "TibiaNewslistV3", jsonData)
135+
}

src/webserver.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ func runWebServer() {
104104
// Tibia killstatistics
105105
v3.GET("/killstatistics/world/:world", TibiaKillstatisticsV3)
106106

107+
// Tibia news
108+
v3.GET("/news/archive", TibiaNewslistV3) // all categories (default 90 days)
109+
v3.GET("/news/archive/:days", TibiaNewslistV3) // all categories
110+
v3.GET("/news/id/:news_id", TibiaNewsV3) // shows one news entry
111+
v3.GET("/news/latest", TibiaNewslistV3) // only news and articles
112+
v3.GET("/news/newsticker", TibiaNewslistV3) // only news_ticker
113+
107114
// Tibia spells
108115
v3.GET("/spells", TibiaSpellsOverviewV3)
109116
v3.GET("/spells/spell/:spell", TibiaSpellsSpellV3)

0 commit comments

Comments
 (0)