131 lines
2.6 KiB
Go
131 lines
2.6 KiB
Go
|
package falseknees
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"math/rand"
|
||
|
"net/http"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
updateInterval = time.Minute * 30
|
||
|
baseURL = "https://www.falseknees.com/"
|
||
|
currentRe = regexp.MustCompile(`window\.location\.href.+"(.+)\.html"`)
|
||
|
imageRe = regexp.MustCompile(`src="(imgs.+\.png)".+title="(.+)"`)
|
||
|
|
||
|
current int
|
||
|
lastUpdate time.Time
|
||
|
)
|
||
|
|
||
|
// Comic is a FalseKnees comic
|
||
|
type Comic struct {
|
||
|
Num int
|
||
|
Title string
|
||
|
Img string
|
||
|
}
|
||
|
|
||
|
// Comic returns a specific Comic
|
||
|
func (c *Client) Comic(ctx context.Context, num int) (*Comic, error) {
|
||
|
return c.comic(ctx, num)
|
||
|
}
|
||
|
|
||
|
// Current returns the current Comic
|
||
|
func (c *Client) Current(ctx context.Context) (*Comic, error) {
|
||
|
if err := c.updateCurrent(ctx); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return c.Comic(ctx, current)
|
||
|
}
|
||
|
|
||
|
// Random returns a random Comic
|
||
|
func (c *Client) Random(ctx context.Context) (*Comic, error) {
|
||
|
if err := c.updateCurrent(ctx); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
rand.Seed(time.Now().UnixNano())
|
||
|
return c.Comic(ctx, rand.Intn(current)+1)
|
||
|
}
|
||
|
|
||
|
func (c *Client) updateCurrent(ctx context.Context) error {
|
||
|
now := time.Now()
|
||
|
if !lastUpdate.IsZero() && lastUpdate.After(now.Add(-updateInterval)) {
|
||
|
return nil
|
||
|
}
|
||
|
lastUpdate = now
|
||
|
|
||
|
u := fmt.Sprintf("%sindex.html", baseURL)
|
||
|
|
||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
resp, err := c.http.Do(req)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if resp.StatusCode != http.StatusOK {
|
||
|
return fmt.Errorf("could not get page for index: %s", resp.Status)
|
||
|
}
|
||
|
|
||
|
body, err := ioutil.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
match := currentRe.FindStringSubmatch(string(body))
|
||
|
if len(match) == 0 {
|
||
|
return errors.New("could not find current comic")
|
||
|
}
|
||
|
|
||
|
curr, err := strconv.Atoi(match[1])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
current = curr
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *Client) comic(ctx context.Context, num int) (*Comic, error) {
|
||
|
u := fmt.Sprintf("%s%d.html", baseURL, num)
|
||
|
|
||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
resp, err := c.http.Do(req)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if resp.StatusCode != http.StatusOK {
|
||
|
return nil, fmt.Errorf("could not get page for comic %d: %s", num, resp.Status)
|
||
|
}
|
||
|
|
||
|
body, err := ioutil.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
match := imageRe.FindStringSubmatch(string(body))
|
||
|
if len(match) == 0 {
|
||
|
return nil, fmt.Errorf("could not find comic #%d", num)
|
||
|
}
|
||
|
|
||
|
return &Comic{
|
||
|
Num: num,
|
||
|
Title: match[2],
|
||
|
Img: fmt.Sprintf("%s%s", baseURL, match[1]),
|
||
|
}, nil
|
||
|
}
|