Skip to content

Feature: add Tibia Forums Section details (v3/forum/section/{name}) #215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions src/TibiaDataUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,19 @@ func TibiaDataDatetimeV3(date string) string {
} else {
// timezone use in html: CET/CEST
loc, _ := time.LoadLocation("Europe/Berlin")

// format used in datetime on html: Jan 02 2007, 19:20:30 CET
formatting := "Jan 02 2006, 15:04:05 MST"
var formatting string

// parsing and setting format of return
switch dateLength := len(date); {
case dateLength > 19:
// format used in datetime on html: Jan 02 2007, 19:20:30 CET
formatting = "Jan 02 2006, 15:04:05 MST"
case dateLength == 19:
// format used in datetime on html: 03.06.2023 01:19:00
formatting = "02.01.2006 15:04:05"
default:
log.Printf("Weird format detected: %s", date)
}

// parsing html in time with location set in loc
returnDate, err = time.ParseInLocation(formatting, date, loc)
Expand Down Expand Up @@ -290,3 +300,19 @@ func TibiaDataGetNewsType(data string) string {
return "unknown"
}
}

// TibiaDataForumNameValidator func - return valid forum string
func TibiaDataForumNameValidator(name string) string {
switch strings.ToLower(name) {
case "world boards", "world", "worldboards":
return "worldboards"
case "trade boards", "trade", "tradeboards":
return "tradeboards"
case "community boards", "community", "communityboards":
return "communityboards"
case "support boards", "support", "supportboards":
return "supportboards"
default:
return "worldboards"
}
}
103 changes: 103 additions & 0 deletions src/TibiaForumSectionV3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package main

import (
"log"
"regexp"
"strings"

"github.com/PuerkitoBio/goquery"
)

type SectionBoardLastPost struct {
ID int `json:"id"`
PostedAt string `json:"posted_at"`
CharacterName string `json:"character_name"`
}

type SectionBoard struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Posts int `json:"posts"`
Threads int `json:"threads"`
LastPost SectionBoardLastPost `json:"last_post"`
}

type ForumSectionResponse struct {
Boards []SectionBoard `json:"boards"`
Information Information `json:"information"`
}

var (
boardInformationRegex = regexp.MustCompile(`.*boardid=(.*)">(.*)<\/a><br\/><font class="ff_info">(.*)<\/font><\/td><td class="TextRight">(.*)<\/td><td class="TextRight">(.*)<\/td><td><span class="LastPostInfo">`)
lastPostIdRegex = regexp.MustCompile(`.*postid=(.*)#post`)
lastPostPostedAtRegex = regexp.MustCompile(`.*height="9"\/><\/a>(.*)<\/span><span><font class="ff_info">`)
lastPostCharacterNameRegex = regexp.MustCompile(`.*subtopic=characters&amp;name=.*\">(.*)<\/a><\/span>`)
)

// TibiaForumSectionV3Impl func
func TibiaForumSectionV3Impl(BoxContentHTML string) ForumSectionResponse {
// Loading HTML data into ReaderHTML for goquery with NewReader
ReaderHTML, err := goquery.NewDocumentFromReader(strings.NewReader(BoxContentHTML))
if err != nil {
log.Fatal(err)
}

var (
BoardsData []SectionBoard
LastPostId int
LastPostPostedAt, LastPostCharacterName string
)

// Running query over each div
ReaderHTML.Find(".TableContentContainer .TableContent tbody tr").Each(func(index int, s *goquery.Selection) {
// Storing HTML into CreatureDivHTML
BoardsDivHTML, err := s.Html()
if err != nil {
log.Fatal(err)
}

subma1 := boardInformationRegex.FindAllStringSubmatch(BoardsDivHTML, -1)
if len(subma1) == 0 {
return
}

subma2 := lastPostIdRegex.FindAllStringSubmatch(BoardsDivHTML, -1)
if len(subma2) > 0 {
LastPostId = TibiaDataStringToIntegerV3(subma2[0][1])
}

subma3 := lastPostPostedAtRegex.FindAllStringSubmatch(BoardsDivHTML, -1)
if len(subma3) > 0 {
LastPostPostedAt = TibiaDataDatetimeV3(strings.Trim(TibiaDataSanitizeStrings(subma3[0][1]), " "))
}

subma4 := lastPostCharacterNameRegex.FindAllStringSubmatch(BoardsDivHTML, -1)
if len(subma4) > 0 {
LastPostCharacterName = TibiaDataSanitizeStrings(subma4[0][1])
}

BoardsData = append(BoardsData, SectionBoard{
ID: TibiaDataStringToIntegerV3(subma1[0][1]),
Name: subma1[0][2],
Description: subma1[0][3],
Posts: TibiaDataStringToIntegerV3(subma1[0][4]),
Threads: TibiaDataStringToIntegerV3(subma1[0][5]),
LastPost: SectionBoardLastPost{
ID: LastPostId,
PostedAt: LastPostPostedAt,
CharacterName: LastPostCharacterName,
},
})
})

//
// Build the data-blob
return ForumSectionResponse{
Boards: BoardsData,
Information: Information{
APIVersion: TibiaDataAPIversion,
Timestamp: TibiaDataDatetimeV3(""),
},
}
}
40 changes: 40 additions & 0 deletions src/TibiaForumSectionV3_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"github.com/stretchr/testify/assert"
"os"
"testing"
)

func TestForums(t *testing.T) {
data, err := os.ReadFile("../testdata/forums/worldboards.html")
if err != nil {
t.Errorf("File reading error: %s", err)
return
}

boardsJson := TibiaForumSectionV3Impl(string(data))
assert := assert.New(t)

assert.Equal(90, len(boardsJson.Boards))

adra := boardsJson.Boards[0]
assert.Equal(146482, adra.ID)
assert.Equal("Adra", adra.Name)
assert.Equal("This board is for general discussions related to the game world Adra.", adra.Description)
assert.Equal(388, adra.Threads)
assert.Equal(1158, adra.Posts)
assert.Equal(39395612, adra.LastPost.ID)
assert.Equal("Rcdohl", adra.LastPost.CharacterName)
assert.Equal("2023-06-02T23:19:00Z", adra.LastPost.PostedAt)

alumbra := boardsJson.Boards[1]
assert.Equal(147016, alumbra.ID)
assert.Equal("Alumbra", alumbra.Name)
assert.Equal("This board is for general discussions related to the game world Alumbra.", alumbra.Description)
assert.Equal(563, alumbra.Threads)
assert.Equal(1011, alumbra.Posts)
assert.Equal(39395777, alumbra.LastPost.ID)
assert.Equal("Mad Mustazza", alumbra.LastPost.CharacterName)
assert.Equal("2023-06-04T15:51:13Z", alumbra.LastPost.PostedAt)
}
29 changes: 29 additions & 0 deletions src/webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ func runWebServer() {
// Tibia worlds
v3.GET("/world/:name", tibiaWorldsWorldV3)
v3.GET("/worlds", tibiaWorldsOverviewV3)

// Tibia forums
v3.GET("/forum/section/:name", tibiaForumSectionV3)
}

// container version details endpoint
Expand Down Expand Up @@ -755,6 +758,32 @@ func tibiaWorldsWorldV3(c *gin.Context) {
"TibiaWorldsWorldV3")
}

// Forum overview godoc
// @Summary List of all forum boards
// @Description Show all boards of Tibia Forum
// @Tags forums
// @Accept json
// @Produce json
// @Success 200 {object} ForumSectionResponse
// @Router /v3/forum/section/{name} [get]
func tibiaForumSectionV3(c *gin.Context) {
// getting params from URL
name := c.Param("name")

tibiadataRequest := TibiaDataRequestStruct{
Method: resty.MethodGet,
URL: "https://www.tibia.com/forum/?subtopic=" + TibiaDataForumNameValidator(name),
}

tibiaDataRequestHandler(
c,
tibiadataRequest,
func(BoxContentHTML string) (interface{}, int) {
return TibiaForumSectionV3Impl(BoxContentHTML), http.StatusOK
},
"TibiaForumOverviewV3")
}

func tibiaDataRequestHandler(c *gin.Context, tibiaDataRequest TibiaDataRequestStruct, requestHandler func(string) (interface{}, int), handlerName string) {
BoxContentHTML, err := TibiaDataHTMLDataCollectorV3(tibiaDataRequest)

Expand Down
Loading