115 lines
2.9 KiB
Go
115 lines
2.9 KiB
Go
package wiki
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// RS3ExchangeItem holds metadata parsed from a Module:Exchange/<name> page.
|
|
type RS3ExchangeItem struct {
|
|
ItemID int
|
|
Name string
|
|
Value int
|
|
Limit int
|
|
Members bool
|
|
Category string
|
|
Examine string
|
|
}
|
|
|
|
// revisionResponse matches the JSON from action=query&prop=revisions&rvslots=main.
|
|
type revisionResponse struct {
|
|
Query struct {
|
|
Pages map[string]struct {
|
|
PageID int `json:"pageid"`
|
|
Title string `json:"title"`
|
|
Revisions []struct {
|
|
Slots struct {
|
|
Main struct {
|
|
Content string `json:"*"`
|
|
} `json:"main"`
|
|
} `json:"slots"`
|
|
} `json:"revisions"`
|
|
} `json:"pages"`
|
|
} `json:"query"`
|
|
}
|
|
|
|
// GetExchangeModule fetches Module:Exchange/<name> and parses item metadata.
|
|
// Returns nil, nil if the module page does not exist (item not tradeable).
|
|
func (c *Client) GetExchangeModule(itemName string) (*RS3ExchangeItem, error) {
|
|
title := "Module:Exchange/" + itemName
|
|
|
|
params := url.Values{
|
|
"action": {"query"},
|
|
"titles": {title},
|
|
"prop": {"revisions"},
|
|
"rvprop": {"content"},
|
|
"rvslots": {"main"},
|
|
}
|
|
|
|
var resp revisionResponse
|
|
if err := c.get(params, &resp); err != nil {
|
|
return nil, fmt.Errorf("fetching exchange module: %w", err)
|
|
}
|
|
|
|
for id, page := range resp.Query.Pages {
|
|
// Page ID -1 means the page doesn't exist
|
|
if id == "-1" {
|
|
return nil, nil
|
|
}
|
|
if len(page.Revisions) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
content := page.Revisions[0].Slots.Main.Content
|
|
return parseLuaModule(content), nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
var (
|
|
reItemID = regexp.MustCompile(`itemId\s*=\s*(\d+)`)
|
|
reItem = regexp.MustCompile(`item\s*=\s*'((?:[^'\\]|\\.)*)'`)
|
|
reValue = regexp.MustCompile(`value\s*=\s*(\d+)`)
|
|
reLimit = regexp.MustCompile(`limit\s*=\s*(\d+)`)
|
|
reMembers = regexp.MustCompile(`members\s*=\s*(true|false)`)
|
|
reCategory = regexp.MustCompile(`category\s*=\s*'((?:[^'\\]|\\.)*)'`)
|
|
reExamine = regexp.MustCompile(`examine\s*=\s*'((?:[^'\\]|\\.)*)'`)
|
|
)
|
|
|
|
func parseLuaModule(content string) *RS3ExchangeItem {
|
|
item := &RS3ExchangeItem{}
|
|
|
|
if m := reItemID.FindStringSubmatch(content); m != nil {
|
|
item.ItemID, _ = strconv.Atoi(m[1])
|
|
}
|
|
if m := reItem.FindStringSubmatch(content); m != nil {
|
|
item.Name = unescapeLua(m[1])
|
|
}
|
|
if m := reValue.FindStringSubmatch(content); m != nil {
|
|
item.Value, _ = strconv.Atoi(m[1])
|
|
}
|
|
if m := reLimit.FindStringSubmatch(content); m != nil {
|
|
item.Limit, _ = strconv.Atoi(m[1])
|
|
}
|
|
if m := reMembers.FindStringSubmatch(content); m != nil {
|
|
item.Members = strings.ToLower(m[1]) == "true"
|
|
}
|
|
if m := reCategory.FindStringSubmatch(content); m != nil {
|
|
item.Category = unescapeLua(m[1])
|
|
}
|
|
if m := reExamine.FindStringSubmatch(content); m != nil {
|
|
item.Examine = unescapeLua(m[1])
|
|
}
|
|
|
|
return item
|
|
}
|
|
|
|
// unescapeLua handles Lua string escape sequences in single-quoted strings.
|
|
func unescapeLua(s string) string {
|
|
return strings.NewReplacer(`\'`, `'`, `\\`, `\`).Replace(s)
|
|
}
|