⏪ Revert removing old version changes
This commit is contained in:
parent
87a95802ea
commit
f4bd348774
45 changed files with 1036 additions and 1262 deletions
28
account.go
28
account.go
|
@ -1,28 +0,0 @@
|
|||
package telegraph
|
||||
|
||||
// Account represents a Telegraph account.
|
||||
type Account struct {
|
||||
// Profile link, opened when users click on the author's name below the
|
||||
// title. Can be any link, not necessarily to a Telegram profile or
|
||||
// channel.
|
||||
AuthorURL URL `json:"author_url"`
|
||||
|
||||
// Optional. URL to authorize a browser on [telegra.ph] and connect it to
|
||||
// a Telegraph account. This URL is valid for only one use and for 5
|
||||
// minutes only.
|
||||
//
|
||||
// [telegra.ph]: https://telegra.ph/
|
||||
AuthURL *URL `json:"auth_url,omitempty"`
|
||||
|
||||
ShortName ShortName `json:"short_name"`
|
||||
|
||||
// Default author name used when creating new articles.
|
||||
AuthorName AuthorName `json:"author_name"`
|
||||
|
||||
// Optional. Only returned by the [createAccount] and
|
||||
// [revokeAccessToken] method. Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
|
||||
// Optional. Number of pages belonging to the Telegraph account.
|
||||
PageCount uint `json:"page_count,omitempty"`
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package telegraph
|
||||
|
||||
type Attributes struct {
|
||||
Href string `json:"href,omitempty"`
|
||||
Src string `json:"src,omitempty"`
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"source.toby3d.me/toby3d/telegraph/internal/util"
|
||||
)
|
||||
|
||||
// AuthorName represent author name used when creating new articles.
|
||||
type AuthorName struct {
|
||||
authorName string // 0-128 characters
|
||||
}
|
||||
|
||||
var ErrAuthorNameLength error = errors.New("unsupported length")
|
||||
|
||||
// NewAuthorName parse raw string as AuthorName and validate it's length.
|
||||
func NewAuthorName(raw string) (*AuthorName, error) {
|
||||
if err := util.ValidateLength(raw, -1, 128); err != nil {
|
||||
return nil, fmt.Errorf("AuthorName: unsupported length: %w", err)
|
||||
}
|
||||
|
||||
return &AuthorName{raw}, nil
|
||||
}
|
||||
|
||||
// IsEmpty returns true if current [AuthorName] is empty.
|
||||
func (an AuthorName) IsEmpty() bool { return an.authorName == "" }
|
||||
|
||||
func (an *AuthorName) UnmarshalJSON(v []byte) error {
|
||||
unquoted, err := strconv.Unquote(string(v))
|
||||
if err != nil {
|
||||
return fmt.Errorf("AuthorName: UnmarshalJSON: cannot unquote value '%s': %w", string(v), err)
|
||||
}
|
||||
|
||||
result, err := NewAuthorName(unquoted)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AuthorName: UnmarshalJSON: cannot parse value '%s': %w", string(v), err)
|
||||
}
|
||||
|
||||
*an = *result
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (an AuthorName) MarshalJSON() ([]byte, error) {
|
||||
if an.authorName != "" {
|
||||
return []byte(strconv.Quote(an.authorName)), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (an AuthorName) String() string {
|
||||
return an.authorName
|
||||
}
|
||||
|
||||
func (an AuthorName) GoString() string {
|
||||
return "telegraph.AuthorName(" + an.String() + ")"
|
||||
}
|
||||
|
||||
func TestAuthorName(tb testing.TB) *AuthorName {
|
||||
tb.Helper()
|
||||
|
||||
return &AuthorName{"Anonymous"}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestNewAuthorName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f := func(name, input string) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual, err := telegraph.NewAuthorName(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if actual.String() != input {
|
||||
t.Errorf("want '%s', got '%s'", input, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
f("empty", "")
|
||||
f("short", "L")
|
||||
f("medium", telegraph.TestAuthorName(t).String())
|
||||
f("long", "Pablo Diego José Francisco de Paula Juan Nepomuceno María de los Remedios Cipriano de la Santísima "+
|
||||
"Trinidad Ruiz y Picasso")
|
||||
}
|
||||
|
||||
func TestAuthorName_IsEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if actual, expect := (telegraph.AuthorName{}).IsEmpty(), true; actual != expect {
|
||||
t.Errorf("want %t, got %t", expect, actual)
|
||||
}
|
||||
}
|
66
content.go
Normal file
66
content.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// ContentFormat transforms data to a DOM-based format to represent the content of the page.
|
||||
func ContentFormat(data interface{}) (n []Node, err error) {
|
||||
var dst *html.Node
|
||||
|
||||
switch src := data.(type) {
|
||||
case string:
|
||||
dst, err = html.Parse(strings.NewReader(src))
|
||||
case []byte:
|
||||
dst, err = html.Parse(bytes.NewReader(src))
|
||||
case io.Reader:
|
||||
dst, err = html.Parse(src)
|
||||
default:
|
||||
return nil, ErrInvalidDataType
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n = append(n, domToNode(dst.FirstChild))
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func domToNode(domNode *html.Node) interface{} {
|
||||
if domNode.Type == html.TextNode {
|
||||
return domNode.Data
|
||||
}
|
||||
|
||||
if domNode.Type != html.ElementNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
nodeElement := new(NodeElement)
|
||||
|
||||
switch strings.ToLower(domNode.Data) {
|
||||
case "a", "aside", "b", "blockquote", "br", "code", "em", "figcaption", "figure", "h3", "h4", "hr", "i",
|
||||
"iframe", "img", "li", "ol", "p", "pre", "s", "strong", "u", "ul", "video":
|
||||
nodeElement.Tag = domNode.Data
|
||||
|
||||
for i := range domNode.Attr {
|
||||
switch strings.ToLower(domNode.Attr[i].Key) {
|
||||
case "href", "src":
|
||||
nodeElement.Attrs = map[string]string{domNode.Attr[i].Key: domNode.Attr[i].Val}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for child := domNode.FirstChild; child != nil; child = child.NextSibling {
|
||||
nodeElement.Children = append(nodeElement.Children, domToNode(child))
|
||||
}
|
||||
|
||||
return nodeElement
|
||||
}
|
28
content_test.go
Normal file
28
content_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestContentFormat(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
_, err := telegraph.ContentFormat(42)
|
||||
assert.EqualError(t, telegraph.ErrInvalidDataType, err.Error())
|
||||
})
|
||||
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
t.Run("string", func(t *testing.T) {
|
||||
validContentDOM, err := telegraph.ContentFormat(`<p>Hello, World!</p>`)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, validContentDOM)
|
||||
})
|
||||
t.Run("bytes", func(t *testing.T) {
|
||||
validContentDOM, err := telegraph.ContentFormat([]byte(`<p>Hello, World!</p>`))
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, validContentDOM)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,36 +1,35 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CreateAccount create a new Telegraph account. Most users only need one
|
||||
// account, but this can be useful for channel administrators who would like to
|
||||
// keep individual author names and profile links for each of their channels. On
|
||||
// success, returns an [Account] object with the regular fields and an
|
||||
// additional access_token field.
|
||||
type CreateAccount struct {
|
||||
// Default profile link, opened when users click on the author's name
|
||||
// below the title. Can be any link, not necessarily to a Telegram
|
||||
// profile or channel.
|
||||
AuthorURL *URL `json:"author_url,omitempty"` // 0-512 characters
|
||||
type createAccount struct {
|
||||
// Account name, helps users with several accounts remember which they are currently using. Displayed to the
|
||||
// user above the "Edit/Publish" button on Telegra.ph, other users don't see this name.
|
||||
ShortName string `json:"short_name"`
|
||||
|
||||
// Default author name used when creating new articles.
|
||||
AuthorName *AuthorName `json:"author_name,omitempty"` // 0-128 characters
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
|
||||
// Required.
|
||||
ShortName ShortName `json:"short_name"` // 1-32 characters
|
||||
// Default profile link, opened when users click on the author's name below the title. Can be any link, not
|
||||
// necessarily to a Telegram profile or channel.
|
||||
AuthorURL string `json:"author_url,omitempty"`
|
||||
}
|
||||
|
||||
func (params CreateAccount) Do(ctx context.Context, client *http.Client) (*Account, error) {
|
||||
data, err := json.Marshal(params)
|
||||
// CreateAccount create a new Telegraph account. Most users only need one account, but this can be useful for channel
|
||||
// administrators who would like to keep individual author names and profile links for each of their channels. On
|
||||
// success, returns an Account object with the regular fields and an additional access_token field.
|
||||
func CreateAccount(account Account) (*Account, error) {
|
||||
data, err := makeRequest("createAccount", createAccount{
|
||||
ShortName: account.ShortName,
|
||||
AuthorName: account.AuthorName,
|
||||
AuthorURL: account.AuthorURL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("createAccount: cannot marshal request parameters: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return post[*Account](ctx, client, bytes.NewReader(data), "createAccount")
|
||||
}
|
||||
result := new(Account)
|
||||
if err = parser.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
32
create_account_test.go
Normal file
32
create_account_test.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestCreateAccount(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
_, err := telegraph.CreateAccount(telegraph.Account{})
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("without shortname", func(t *testing.T) {
|
||||
_, err := telegraph.CreateAccount(telegraph.Account{
|
||||
ShortName: "",
|
||||
AuthorName: "Anonymous",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
})
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
account, err := telegraph.CreateAccount(telegraph.Account{
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, account)
|
||||
})
|
||||
}
|
|
@ -1,41 +1,44 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CreatePage create a new Telegraph page. On success, returns a [Page] object.
|
||||
type CreatePage struct {
|
||||
// Profile link, opened when users click on the author's name below the
|
||||
// title. Can be any link, not necessarily to a Telegram profile or
|
||||
// channel.
|
||||
AuthorURL *URL `json:"author_url,omitempty"` // 0-512 characters
|
||||
|
||||
// Required. Access token of the Telegraph account.
|
||||
type createPage struct {
|
||||
// Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token"`
|
||||
|
||||
// Required. Page title.
|
||||
Title Title `json:"title"` // 1-256 characters
|
||||
// Page title.
|
||||
Title string `json:"title"`
|
||||
|
||||
// Author name, displayed below the article's title.
|
||||
AuthorName *AuthorName `json:"author_name,omitempty"` // 0-128 characters
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
|
||||
// Required. Content of the page.
|
||||
Content []Node `json:"content"` // up to 64 KB
|
||||
// Profile link, opened when users click on the author's name below the title. Can be any link, not
|
||||
// necessarily to a Telegram profile or channel.
|
||||
AuthorURL string `json:"author_url,omitempty"`
|
||||
|
||||
// Content of the page.
|
||||
Content []Node `json:"content"`
|
||||
|
||||
// If true, a content field will be returned in the Page object.
|
||||
ReturnContent bool `json:"return_content,omitempty"` // false
|
||||
ReturnContent bool `json:"return_content,omitempty"`
|
||||
}
|
||||
|
||||
func (params CreatePage) Do(ctx context.Context, client *http.Client) (*Page, error) {
|
||||
data, err := json.Marshal(params)
|
||||
// CreatePage create a new Telegraph page. On success, returns a Page object.
|
||||
func (a *Account) CreatePage(page Page, returnContent bool) (*Page, error) {
|
||||
data, err := makeRequest("createPage", createPage{
|
||||
AccessToken: a.AccessToken,
|
||||
Title: page.Title,
|
||||
AuthorName: page.AuthorName,
|
||||
AuthorURL: page.AuthorURL,
|
||||
Content: page.Content,
|
||||
ReturnContent: returnContent,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("createPage: cannot marshal request parameters: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return post[*Page](ctx, client, bytes.NewReader(data), "createPage")
|
||||
}
|
||||
result := new(Page)
|
||||
if err = parser.Unmarshal(data, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
40
create_page_test.go
Normal file
40
create_page_test.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestCreatePage(t *testing.T) {
|
||||
content, err := telegraph.ContentFormat(`<p>Hello, world!</p>`)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
var a telegraph.Account
|
||||
_, err := a.CreatePage(telegraph.Page{
|
||||
Title: "Sample Page",
|
||||
AuthorName: "Anonymous",
|
||||
Content: content,
|
||||
}, true)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
a := telegraph.Account{
|
||||
AccessToken: "b968da509bb76866c35425099bc0989a5ec3b32997d55286c657e6994bbb",
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
}
|
||||
|
||||
page, err := a.CreatePage(telegraph.Page{
|
||||
Title: "Sample Page",
|
||||
AuthorName: "Anonymous",
|
||||
Content: content,
|
||||
}, true)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.NotEmpty(t, page.URL)
|
||||
})
|
||||
}
|
33
doc.go
33
doc.go
|
@ -1,21 +1,14 @@
|
|||
// The telegraph package contains the base bindings for working with the
|
||||
// Telegraph API.
|
||||
//
|
||||
// Telegra.ph is a minimalist publishing tool that allows you to create richly
|
||||
// formatted posts and push them to the Web in just a click. Telegraph posts
|
||||
// also get beautiful [Instant View] pages on Telegram.
|
||||
//
|
||||
// To maintain the purity of the basic interface, we launched the [@Telegraph]
|
||||
// bot for those who require advanced features. This bot can help you manage
|
||||
// your articles across any number of devices and get page view statistics for
|
||||
// any Telegraph page.
|
||||
//
|
||||
// Anyone can enjoy the simplicity of Telegraph publishing, not just [Telegram]
|
||||
// users. For this reason, all developers are welcome to use this Telegraph API
|
||||
// to create bots like [@Telegraph] for any other platform, or even standalone
|
||||
// interfaces.
|
||||
//
|
||||
// [Instant View]: https://telegram.org/blog/instant-view
|
||||
// [@Telegraph]: https://telegram.me/telegraph
|
||||
// [Telegram]: https://telegram.org/
|
||||
/*
|
||||
Package telegraph has functions and types used for interacting with the Telegraph API.
|
||||
|
||||
Telegra.ph is a minimalist publishing tool that allows you to create richly formatted posts and push them to the Web
|
||||
in just a click. Telegraph posts also get beautiful Instant View pages on Telegram.
|
||||
|
||||
To maintain the purity of the basic interface, we launched the @Telegraph bot for those who require advanced features.
|
||||
This bot can help you manage your articles across any number of devices and get page view statistics for any Telegraph
|
||||
page.
|
||||
|
||||
Anyone can enjoy the simplicity of Telegraph publishing, not just Telegram users. For this reason, all developers are
|
||||
welcome to use this Telegraph API to create bots like @Telegraph for any other platform, or even standalone interfaces.
|
||||
*/
|
||||
package telegraph // import "source.toby3d.me/toby3d/telegraph"
|
|
@ -1,38 +1,37 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// EditAccountInfo update information about a Telegraph account. Pass only the
|
||||
// parameters that you want to edit. On success, returns an [Account] object
|
||||
// with the default fields.
|
||||
type EditAccountInfo struct {
|
||||
// New default profile link, opened when users click on the author's
|
||||
// name below the title. Can be any link, not necessarily to a Telegram
|
||||
// profile or channel.
|
||||
AuthorURL *url.URL `json:"author_url,omitempty"` // 0-512 characters
|
||||
type editAccountInfo struct {
|
||||
// Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token"`
|
||||
|
||||
// New account name.
|
||||
ShortName *ShortName `json:"short_name,omitempty"` // 1-32 characters
|
||||
ShortName string `json:"short_name,omitempty"`
|
||||
|
||||
// New default author name used when creating new articles.
|
||||
AuthorName *AuthorName `json:"author_name,omitempty"` // 0-128 characters
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
|
||||
// Required. Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token"`
|
||||
// New default profile link, opened when users click on the author's name below the title. Can be any link,
|
||||
// not necessarily to a Telegram profile or channel.
|
||||
AuthorURL string `json:"author_url,omitempty"`
|
||||
}
|
||||
|
||||
func (params EditAccountInfo) Do(ctx context.Context, client *http.Client) (*Account, error) {
|
||||
data, err := json.Marshal(params)
|
||||
// EditAccountInfo update information about a Telegraph account. Pass only the parameters that you want to edit. On
|
||||
// success, returns an Account object with the default fields.
|
||||
func (a *Account) EditAccountInfo(update Account) (*Account, error) {
|
||||
data, err := makeRequest("editAccountInfo", editAccountInfo{
|
||||
AccessToken: a.AccessToken,
|
||||
ShortName: update.ShortName,
|
||||
AuthorName: update.AuthorName,
|
||||
AuthorURL: update.AuthorURL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("editAccountInfo: cannot marshal request parameters: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return post[*Account](ctx, client, bytes.NewReader(data), "editAccountInfo")
|
||||
}
|
||||
result := new(Account)
|
||||
if err = parser.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
29
edit_account_info_test.go
Normal file
29
edit_account_info_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestEditAccountInfo(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
var a telegraph.Account
|
||||
_, err := a.EditAccountInfo(telegraph.Account{})
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
a := telegraph.Account{
|
||||
AccessToken: "b968da509bb76866c35425099bc0989a5ec3b32997d55286c657e6994bbb",
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
}
|
||||
|
||||
_, err := a.EditAccountInfo(telegraph.Account{
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
61
edit_page.go
61
edit_page.go
|
@ -1,45 +1,52 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
// EditPage edit an existing Telegraph page. On success, returns a [Page]
|
||||
// object.
|
||||
type EditPage struct {
|
||||
// Profile link, opened when users click on the author's name below the
|
||||
// title. Can be any link, not necessarily to a Telegram profile or
|
||||
// channel.
|
||||
AuthorURL *URL `json:"author_url,omitempty"` // 0-512 characters
|
||||
|
||||
// Required. Access token of the Telegraph account.
|
||||
type editPage struct {
|
||||
// Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token"`
|
||||
|
||||
// Required. Path to the page.
|
||||
// Path to the page.
|
||||
Path string `json:"path"`
|
||||
|
||||
// Required. Page title.
|
||||
Title Title `json:"title"` // 1-256 characters
|
||||
// Page title.
|
||||
Title string `json:"title"`
|
||||
|
||||
// Content of the page.
|
||||
Content []Node `json:"content"`
|
||||
|
||||
// Author name, displayed below the article's title.
|
||||
AuthorName *AuthorName `json:"author_name,omitempty"` // 0-128 characters
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
|
||||
// Required. [Content] of the page.
|
||||
Content []Node `json:"content"` // up to 64 KB
|
||||
// Profile link, opened when users click on the author's name below the title. Can be any link, not
|
||||
// necessarily to a Telegram profile or channel.
|
||||
AuthorURL string `json:"author_url,omitempty"`
|
||||
|
||||
// If true, a content field will be returned in the [Page] object.
|
||||
ReturnContent bool `json:"return_content,omitempty"` // false
|
||||
// If true, a content field will be returned in the Page object.
|
||||
ReturnContent bool `json:"return_content,omitempty"`
|
||||
}
|
||||
|
||||
func (params EditPage) EditPage(ctx context.Context, client *http.Client) (*Page, error) {
|
||||
data, err := json.Marshal(params)
|
||||
// EditPage edit an existing Telegraph page. On success, returns a Page object.
|
||||
func (a *Account) EditPage(update Page, returnContent bool) (*Page, error) {
|
||||
data, err := makeRequest(path.Join("editPage", update.Path), editPage{
|
||||
AccessToken: a.AccessToken,
|
||||
Path: update.Path,
|
||||
Title: update.Title,
|
||||
Content: update.Content,
|
||||
AuthorName: update.AuthorName,
|
||||
AuthorURL: update.AuthorURL,
|
||||
ReturnContent: returnContent,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("editPage: cannot marshal request parameters: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return post[*Page](ctx, client, bytes.NewReader(data), "editPage", params.Path)
|
||||
}
|
||||
result := new(Page)
|
||||
if err = parser.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
39
edit_page_test.go
Normal file
39
edit_page_test.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestEditPage(t *testing.T) {
|
||||
content, err := telegraph.ContentFormat("<p>Hello, world!</p>")
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
var a telegraph.Account
|
||||
_, err := a.EditPage(telegraph.Page{
|
||||
Title: "Sample Page",
|
||||
AuthorName: "Anonymous",
|
||||
Content: content,
|
||||
}, true)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
a := telegraph.Account{
|
||||
AccessToken: "b968da509bb76866c35425099bc0989a5ec3b32997d55286c657e6994bbb",
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
}
|
||||
|
||||
page, err := a.EditPage(telegraph.Page{
|
||||
Path: "Sample-Page-12-15",
|
||||
Title: "Sample Page",
|
||||
AuthorName: "Anonymous",
|
||||
Content: content,
|
||||
}, true)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, page.Content)
|
||||
})
|
||||
}
|
193
example_test.go
Normal file
193
example_test.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
// Content in a string format (for this example).
|
||||
// Be sure to wrap every media in a <figure> tag, okay? Be easy.
|
||||
const data = `
|
||||
<figure>
|
||||
<img src="/file/6a5b15e7eb4d7329ca7af.jpg"/>
|
||||
</figure>
|
||||
<p><i>Hello</i>, my name is <b>Page</b>, <u>look at me</u>!</p>
|
||||
<figure>
|
||||
<iframe src="https://youtu.be/fzQ6gRAEoy0"></iframe>
|
||||
<figcaption>
|
||||
Yes, you can embed youtube, vimeo and twitter widgets too!
|
||||
</figcaption>
|
||||
</figure>
|
||||
`
|
||||
|
||||
var (
|
||||
account *telegraph.Account //nolint:gochecknoglobals
|
||||
page *telegraph.Page //nolint:gochecknoglobals
|
||||
content []telegraph.Node //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
func errCheck(err error) {
|
||||
if err != nil {
|
||||
log.Fatalln(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func Example_fastStart() {
|
||||
var err error
|
||||
// Create new Telegraph account.
|
||||
requisites := telegraph.Account{
|
||||
ShortName: "toby3d", // required
|
||||
|
||||
// Author name/link can be epmty. So secure. Much anonymously. Wow.
|
||||
AuthorName: "Maxim Lebedev", // optional
|
||||
AuthorURL: "https://t.me/toby3d", // optional
|
||||
}
|
||||
account, err = telegraph.CreateAccount(requisites)
|
||||
errCheck(err)
|
||||
|
||||
// Make sure that you have saved acc.AuthToken for create new pages or make
|
||||
// other actions by this account in next time!
|
||||
|
||||
// Format content to []telegraph.Node array. Input data can be string, []byte
|
||||
// or io.Reader.
|
||||
content, err = telegraph.ContentFormat(data)
|
||||
errCheck(err)
|
||||
|
||||
// Boom!.. And your text will be understandable for Telegraph. MAGIC.
|
||||
|
||||
// Create new Telegraph page
|
||||
pageData := telegraph.Page{
|
||||
Title: "My super-awesome page", // required
|
||||
Content: content, // required
|
||||
|
||||
// Not necessarily, but, hey, it's just an example.
|
||||
AuthorName: account.AuthorName, // optional
|
||||
AuthorURL: account.AuthorURL, // optional
|
||||
}
|
||||
page, err = account.CreatePage(pageData, false)
|
||||
errCheck(err)
|
||||
|
||||
// Show link from response on created page.
|
||||
log.Println("Kaboom! Page created, look what happened:", page.URL)
|
||||
}
|
||||
|
||||
func ExampleCreateAccount() {
|
||||
var err error
|
||||
account, err = telegraph.CreateAccount(telegraph.Account{
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
})
|
||||
errCheck(err)
|
||||
|
||||
log.Println("AccessToken:", account.AccessToken)
|
||||
log.Println("AuthURL:", account.AuthorURL)
|
||||
log.Println("ShortName:", account.ShortName)
|
||||
log.Println("AuthorName:", account.AuthorName)
|
||||
}
|
||||
|
||||
func ExampleAccount_EditAccountInfo() {
|
||||
var err error
|
||||
account, err = account.EditAccountInfo(telegraph.Account{
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
})
|
||||
errCheck(err)
|
||||
|
||||
log.Println("AuthURL:", account.AuthorURL)
|
||||
log.Println("ShortName:", account.ShortName)
|
||||
log.Println("AuthorName:", account.AuthorName)
|
||||
}
|
||||
|
||||
func ExampleAccount_GetAccountInfo() {
|
||||
info, err := account.GetAccountInfo(
|
||||
telegraph.FieldShortName,
|
||||
telegraph.FieldPageCount,
|
||||
)
|
||||
errCheck(err)
|
||||
|
||||
log.Println("ShortName:", info.ShortName)
|
||||
log.Println("PageCount:", info.PageCount, "pages")
|
||||
}
|
||||
|
||||
func ExampleAccount_RevokeAccessToken() {
|
||||
var err error
|
||||
// You must rewrite current variable with account structure for further usage.
|
||||
account, err = account.RevokeAccessToken()
|
||||
errCheck(err)
|
||||
|
||||
log.Println("AccessToken:", account.AccessToken)
|
||||
}
|
||||
|
||||
func ExampleAccount_CreatePage() {
|
||||
var err error
|
||||
page, err = account.CreatePage(telegraph.Page{
|
||||
Title: "Sample Page",
|
||||
AuthorName: account.AuthorName,
|
||||
Content: content,
|
||||
}, true)
|
||||
errCheck(err)
|
||||
|
||||
log.Println(page.Title, "by", page.AuthorName, "has been created!")
|
||||
log.Println("PageURL:", page.URL)
|
||||
}
|
||||
|
||||
func ExampleAccount_EditPage() {
|
||||
var err error
|
||||
page, err = account.EditPage(telegraph.Page{
|
||||
Title: "Sample Page",
|
||||
AuthorName: account.AuthorName,
|
||||
Content: content,
|
||||
}, true)
|
||||
errCheck(err)
|
||||
|
||||
log.Println("Page on", page.Path, "path has been updated!")
|
||||
log.Println("PageURL:", page.URL)
|
||||
}
|
||||
|
||||
func ExampleGetPage() {
|
||||
info, err := telegraph.GetPage("Sample-Page-12-15", true)
|
||||
errCheck(err)
|
||||
|
||||
log.Println("Getted info about", info.Path, "page:")
|
||||
log.Println("Author:", info.AuthorName)
|
||||
log.Println("Views:", info.Views)
|
||||
log.Println("CanEdit:", info.CanEdit)
|
||||
}
|
||||
|
||||
func ExampleAccount_GetPageList() {
|
||||
list, err := account.GetPageList(0, 3)
|
||||
errCheck(err)
|
||||
|
||||
log.Println("Getted", list.TotalCount, "pages")
|
||||
|
||||
for i := range list.Pages {
|
||||
p := list.Pages[i]
|
||||
log.Printf("%s: %s\n~ %s\n\n", p.Title, p.URL, p.Description)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleGetViews() {
|
||||
pagePath := "Sample-Page-12-15"
|
||||
dateTime := time.Date(2016, time.December, 0, 0, 0, 0, 0, time.UTC)
|
||||
views, err := telegraph.GetViews(pagePath, dateTime)
|
||||
errCheck(err)
|
||||
|
||||
log.Println(pagePath, "has been viewed", views.Views, "times")
|
||||
}
|
||||
|
||||
func ExampleContentFormat() {
|
||||
const data = `<figure>
|
||||
<img src="http://telegra.ph/file/6a5b15e7eb4d7329ca7af.jpg" /></figure>
|
||||
<p><i>Hello</i>, my name is <b>Page</b>, <u>look at me</u>!</p>
|
||||
<figure><iframe src="https://youtu.be/fzQ6gRAEoy0"></iframe>
|
||||
<figcaption>Yes, you can embed youtube, vimeo and twitter widgets too!</figcaption>
|
||||
</figure>`
|
||||
|
||||
var err error
|
||||
content, err = telegraph.ContentFormat(data)
|
||||
errCheck(err)
|
||||
|
||||
log.Printf("Content: %#v", content)
|
||||
}
|
|
@ -1,58 +1,27 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
type getAccountInfo struct {
|
||||
// Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token"`
|
||||
|
||||
type (
|
||||
// GetAccountInfo get information about a Telegraph account. Returns an
|
||||
// [Account] object on success.
|
||||
GetAccountInfo struct {
|
||||
// Required. Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token"`
|
||||
|
||||
// List of account fields to return.
|
||||
Fields []AccountField `json:"fields,omitempty"` // ["short_name","author_name","author_url"]
|
||||
}
|
||||
|
||||
AccountField struct{ accountField string }
|
||||
)
|
||||
|
||||
var (
|
||||
AuthorNameField AccountField = AccountField{"author_name"}
|
||||
AuthorURLField AccountField = AccountField{"author_url"}
|
||||
AuthURLField AccountField = AccountField{"auth_url"}
|
||||
PageCountField AccountField = AccountField{"page_count"}
|
||||
ShortNameField AccountField = AccountField{"short_name"}
|
||||
)
|
||||
|
||||
func (params GetAccountInfo) Do(ctx context.Context, client *http.Client) (*Account, error) {
|
||||
data := make(url.Values)
|
||||
params.populate(data)
|
||||
|
||||
return get[*Account](ctx, client, data, "getAccountInfo")
|
||||
// List of account fields to return.
|
||||
Fields []string `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
func (p GetAccountInfo) populate(dst url.Values) {
|
||||
dst.Set("access_token", p.AccessToken)
|
||||
|
||||
if len(p.Fields) == 0 {
|
||||
return
|
||||
// GetAccountInfo get information about a Telegraph account. Returns an Account object on success.
|
||||
func (a *Account) GetAccountInfo(fields ...string) (*Account, error) {
|
||||
data, err := makeRequest("getAccountInfo", getAccountInfo{
|
||||
AccessToken: a.AccessToken,
|
||||
Fields: fields,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
values := make([]string, 0, len(p.Fields))
|
||||
|
||||
for i := range p.Fields {
|
||||
if p.Fields[i].accountField == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
values = append(values, strconv.Quote(p.Fields[i].accountField))
|
||||
result := new(Account)
|
||||
if err = parser.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dst.Set("fields", "["+strings.Join(values, ",")+"]")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
28
get_account_info_test.go
Normal file
28
get_account_info_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestGetAccountInfo(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
var a telegraph.Account
|
||||
_, err := a.GetAccountInfo()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
a := telegraph.Account{
|
||||
AccessToken: "b968da509bb76866c35425099bc0989a5ec3b32997d55286c657e6994bbb",
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
}
|
||||
|
||||
info, err := a.GetAccountInfo(telegraph.FieldShortName, telegraph.FieldPageCount)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, a.ShortName, info.ShortName)
|
||||
assert.NotZero(t, info.PageCount)
|
||||
})
|
||||
}
|
34
get_page.go
34
get_page.go
|
@ -1,27 +1,31 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
gopath "path"
|
||||
)
|
||||
|
||||
// GetPage get a Telegraph page. Returns a [Page] object on success.
|
||||
type GetPage struct {
|
||||
// Required. Path to the Telegraph page (in the format Title-12-31, i.e.
|
||||
// everything that comes after http://telegra.ph/).
|
||||
type getPage struct {
|
||||
// Path to the Telegraph page (in the format Title-12-31, i.e. everything that comes after http://telegra.ph/).
|
||||
Path string `json:"path"`
|
||||
|
||||
// If true, content field will be returned in [Page] object.
|
||||
// If true, content field will be returned in Page object.
|
||||
ReturnContent bool `json:"return_content,omitempty"`
|
||||
}
|
||||
|
||||
func (params GetPage) Do(ctx context.Context, client *http.Client) (*Page, error) {
|
||||
data := make(url.Values)
|
||||
|
||||
if params.ReturnContent {
|
||||
data.Set("return_content", "true")
|
||||
// GetPage get a Telegraph page. Returns a Page object on success.
|
||||
func GetPage(path string, returnContent bool) (*Page, error) {
|
||||
data, err := makeRequest(gopath.Join("getPage", path), getPage{
|
||||
Path: path,
|
||||
ReturnContent: returnContent,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return get[*Page](ctx, client, data, "getPage", params.Path)
|
||||
}
|
||||
result := new(Page)
|
||||
if err = parser.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -1,33 +1,32 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// GetPageList get a list of pages belonging to a Telegraph account. Returns a
|
||||
// [PageList] object, sorted by most recently created pages first.
|
||||
type GetPageList struct {
|
||||
// Required. Access token of the Telegraph account.
|
||||
type getPageList struct {
|
||||
// Access token of the Telegraph account.
|
||||
AccessToken string `json:"access_token"`
|
||||
|
||||
// Sequential number of the first page to be returned.
|
||||
Offset uint `json:"offset,omitempty"` // 0
|
||||
Offset int `json:"offset,omitempty"`
|
||||
|
||||
// Limits the number of pages to be retrieved.
|
||||
Limit uint16 `json:"limit,omitempty"` // 50 (0-200)
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
func (params GetPageList) Do(ctx context.Context, client *http.Client) (*PageList, error) {
|
||||
data := make(url.Values)
|
||||
data.Set("access_token", params.AccessToken)
|
||||
data.Set("offset", strconv.FormatUint(uint64(params.Offset), 10))
|
||||
|
||||
if params.Limit <= 200 {
|
||||
data.Set("limit", strconv.FormatUint(uint64(params.Limit), 10))
|
||||
// GetPageList get a list of pages belonging to a Telegraph account. Returns a PageList object, sorted by most
|
||||
// recently created pages first.
|
||||
func (a *Account) GetPageList(offset, limit int) (*PageList, error) {
|
||||
data, err := makeRequest("getPageList", getPageList{
|
||||
AccessToken: a.AccessToken,
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return get[*PageList](ctx, client, data, "getPageList")
|
||||
}
|
||||
result := new(PageList)
|
||||
if err = parser.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
27
get_page_list_test.go
Normal file
27
get_page_list_test.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestGetPageList(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
var a telegraph.Account
|
||||
_, err := a.GetPageList(0, 0)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
a := telegraph.Account{
|
||||
AccessToken: "b968da509bb76866c35425099bc0989a5ec3b32997d55286c657e6994bbb",
|
||||
ShortName: "Sandbox",
|
||||
AuthorName: "Anonymous",
|
||||
}
|
||||
|
||||
list, err := a.GetPageList(1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, list)
|
||||
})
|
||||
}
|
20
get_page_test.go
Normal file
20
get_page_test.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestGetPage(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
_, err := telegraph.GetPage("wtf", true)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
page, err := telegraph.GetPage("Sample-Page-12-15", true)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, page)
|
||||
})
|
||||
}
|
97
get_views.go
97
get_views.go
|
@ -1,75 +1,50 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
gopath "path"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetViews get the number of views for a Telegraph article. Returns a
|
||||
// [PageViews] object on success. By default, the total number of page views
|
||||
// will be returned.
|
||||
type GetViews struct {
|
||||
// Required. Path to the Telegraph page (in the format Title-12-31,
|
||||
// where 12 is the month and 31 the day the article was first
|
||||
// published).
|
||||
Path string `json:"-"`
|
||||
type getViews struct {
|
||||
// Path to the Telegraph page (in the format Title-12-31, where 12 is the month and 31 the day the article was
|
||||
// first published).
|
||||
Path string `json:"path"`
|
||||
|
||||
// Required if month is passed. If passed, the number of page views for
|
||||
// the requested year will be returned.
|
||||
Year uint16 `json:"year,omitempty"` // 2000-2100
|
||||
// Required if month is passed. If passed, the number of page views for the requested year will be returned.
|
||||
Year int `json:"year,omitempty"`
|
||||
|
||||
// Required if day is passed. If passed, the number of page views for
|
||||
// the requested month will be returned.
|
||||
Month uint8 `json:"month,omitempty"` // 1-12
|
||||
// Required if day is passed. If passed, the number of page views for the requested month will be returned.
|
||||
Month int `json:"month,omitempty"`
|
||||
|
||||
// Required if hour is passed. If passed, the number of page views for
|
||||
// the requested day will be returned.
|
||||
Day uint8 `json:"day,omitempty"` // 1-31
|
||||
// Required if hour is passed. If passed, the number of page views for the requested day will be returned.
|
||||
Day int `json:"day,omitempty"`
|
||||
|
||||
// If passed, the number of page views for the requested hour will be
|
||||
// returned.
|
||||
Hour uint8 `json:"hour,omitempty"` // 0-24
|
||||
// If passed, the number of page views for the requested hour will be returned.
|
||||
Hour int `json:"hour,omitempty"`
|
||||
}
|
||||
|
||||
func (params GetViews) Do(ctx context.Context, client *http.Client) (*PageViews, error) {
|
||||
data := make(url.Values)
|
||||
// GetViews get the number of views for a Telegraph article. By default, the total number of page views will be
|
||||
// returned. Returns a PageViews object on success.
|
||||
func GetViews(path string, date time.Time) (*PageViews, error) {
|
||||
p := new(getViews)
|
||||
p.Path = path
|
||||
|
||||
switch {
|
||||
case 0 < params.Year:
|
||||
if params.Year < 2000 {
|
||||
params.Year = 2000
|
||||
} else if 2100 < params.Year {
|
||||
params.Year = 2100
|
||||
}
|
||||
|
||||
data.Set("year", strconv.FormatUint(uint64(params.Year), 10))
|
||||
|
||||
fallthrough
|
||||
case 0 < params.Month:
|
||||
if 12 < params.Month {
|
||||
params.Month = 12
|
||||
}
|
||||
|
||||
data.Set("month", strconv.FormatUint(uint64(params.Month), 10))
|
||||
|
||||
fallthrough
|
||||
case 0 < params.Day:
|
||||
if 31 < params.Day {
|
||||
params.Day = 31
|
||||
}
|
||||
|
||||
data.Set("day", strconv.FormatUint(uint64(params.Day), 10))
|
||||
|
||||
fallthrough
|
||||
case 0 < params.Hour:
|
||||
if 24 < params.Hour {
|
||||
params.Hour = 24
|
||||
}
|
||||
|
||||
data.Set("hour", strconv.FormatUint(uint64(params.Hour), 10))
|
||||
if !date.IsZero() {
|
||||
p.Year = date.Year()
|
||||
p.Month = int(date.Month())
|
||||
p.Day = date.Day()
|
||||
p.Hour = date.Hour()
|
||||
}
|
||||
|
||||
return get[*PageViews](ctx, client, data, "getViews", params.Path)
|
||||
}
|
||||
data, err := makeRequest(gopath.Join("getViews", path), p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := new(PageViews)
|
||||
if err = parser.Unmarshal(data, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
51
get_views_test.go
Normal file
51
get_views_test.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package telegraph_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"source.toby3d.me/toby3d/telegraph"
|
||||
)
|
||||
|
||||
func TestGetViews(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
t.Run("path", func(t *testing.T) {
|
||||
_, err := telegraph.GetViews("wtf", time.Time{})
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("year", func(t *testing.T) {
|
||||
dt := time.Date(1980, 0, 0, 0, 0, 0, 0, time.UTC)
|
||||
_, err := telegraph.GetViews("Sample-Page-12-15", dt)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("month", func(t *testing.T) {
|
||||
dt := time.Date(2000, 22, 0, 0, 0, 0, 0, time.UTC)
|
||||
result, err := telegraph.GetViews("Sample-Page-12-15", dt)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
})
|
||||
t.Run("day", func(t *testing.T) {
|
||||
dt := time.Date(2000, time.February, 42, 0, 0, 0, 0, time.UTC)
|
||||
result, err := telegraph.GetViews("Sample-Page-12-15", dt)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
})
|
||||
t.Run("hour", func(t *testing.T) {
|
||||
dt := time.Date(2000, time.February, 12, 65, 0, 0, 0, time.UTC)
|
||||
result, err := telegraph.GetViews("Sample-Page-12-15", dt)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
})
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
dt := time.Date(2016, time.December, 31, 0, 0, 0, 0, time.UTC)
|
||||
stats, err := telegraph.GetViews("Sample-Page-12-15", dt)
|
||||
assert.NoError(t, err)
|
||||
if !assert.NotNil(t, stats) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.NotZero(t, stats.Views)
|
||||
})
|
||||
}
|
11
go.mod
11
go.mod
|
@ -1,8 +1,13 @@
|
|||
module source.toby3d.me/toby3d/telegraph
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.6.0
|
||||
golang.org/x/net v0.27.0
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/klauspost/compress v1.10.9 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/valyala/fasthttp v1.14.0
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9
|
||||
)
|
||||
|
||||
go 1.22.0
|
||||
go 1.13
|
||||
|
|
38
go.sum
38
go.sum
|
@ -1,4 +1,34 @@
|
|||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4=
|
||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.10.9 h1:pPRt1Z78crspaHISkpSSHjDlx+Tt9suHe519dsI0vF4=
|
||||
github.com/klauspost/compress v1.10.9/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.14.0 h1:67bfuW9azCMwW/Jlq/C+VeihNpAuJMWkYPBig1gdi3A=
|
||||
github.com/valyala/fasthttp v1.14.0/go.mod h1:ol1PCaL0dX20wC0htZ7sYCsvCYmrouYra0zHzaclZhE=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
70
node.go
70
node.go
|
@ -1,70 +0,0 @@
|
|||
package telegraph
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Node represent abstract object represents a DOM Node. It can be a String
|
||||
// which represents a DOM text node or a [NodeElement] object.
|
||||
type Node struct {
|
||||
Element *NodeElement `json:"-"`
|
||||
Text string `json:"-"`
|
||||
}
|
||||
|
||||
func (n *Node) UnmarshalJSON(v []byte) error {
|
||||
switch v[0] {
|
||||
case '{':
|
||||
nodeElement := new(NodeElement)
|
||||
if err := json.Unmarshal(v, nodeElement); err != nil {
|
||||
return fmt.Errorf("Node: UnmarshalJSON: cannot unmarshal NodeElement: %w", err)
|
||||
}
|
||||
|
||||
n.Element = nodeElement
|
||||
case '"':
|
||||
unquoted, err := strconv.Unquote(string(v))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Node: UnmarshalJSON: cannot unquote string: %w", err)
|
||||
}
|
||||
|
||||
n.Text = unquoted
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n Node) MarshalJSON() ([]byte, error) {
|
||||
switch {
|
||||
default:
|
||||
return nil, nil
|
||||
case n.Text != "":
|
||||
return []byte(strconv.Quote(n.Text)), nil
|
||||
case n.Element != nil:
|
||||
result, err := json.Marshal(n.Element)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Node: MarshalJSON: cannot encode as Element: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n Node) String() string {
|
||||
switch {
|
||||
default:
|
||||
return ""
|
||||
case n.Text != "":
|
||||