diff --git a/.github/workflows/auto-close-pr.yaml b/.github/workflows/auto-close-pr.yaml new file mode 100644 index 0000000..7e59f0b --- /dev/null +++ b/.github/workflows/auto-close-pr.yaml @@ -0,0 +1,18 @@ +name: AutoMigrate issues +on: issues +jobs: + default: + steps: + - name: AutoClose issue + uses: superbrothers/auto-close-action@v0.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMENT: | + 🚚 Please, [reopen your PR on GitLab](https://gitlab.com/toby3d/telegraph/merge_requests). + + ## Why? + Microsoft is acquiring GitHub. I don't want to be a part of this. I have already moved all of my code to GitLab and I will not update any of my github repos anymore. + + If you have opened an issue or PR on one of my repos, it's still there, and it will be for a while, but please keep the conversations going on GitLab instead. + + Microsoft is an enemy of Open Source and Free Software, and I will do my part in moving away from it in any way I can. \ No newline at end of file diff --git a/.github/workflows/auto-migrate-issue.yaml b/.github/workflows/auto-migrate-issue.yaml new file mode 100644 index 0000000..2216b84 --- /dev/null +++ b/.github/workflows/auto-migrate-issue.yaml @@ -0,0 +1,17 @@ +name: AutoMigrate issues +on: issues +jobs: + default: + steps: + - name: AutoClose issue + uses: superbrothers/auto-close-action@v0.0.1 + env: + comment: | + 🚚 Please, [reopen your PR on GitLab](https://gitlab.com/toby3d/telegraph/merge_requests). + + ## Why? + Microsoft is acquiring GitHub. I don't want to be a part of this. I have already moved all of my code to GitLab and I will not update any of my github repos anymore. + + If you have opened an issue or PR on one of my repos, it's still there, and it will be for a while, but please keep the conversations going on GitLab instead. + + Microsoft is an enemy of Open Source and Free Software, and I will do my part in moving away from it in any way I can. \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7e03afd..defb1a6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,17 +1,25 @@ image: golang:alpine +variables: + GO111MODULE: "on" + CGO_ENABLED: "0" + GOOS: "linux" + +cache: + key: ${CI_COMMIT_REF_SLUG} + paths: + - /var/cache/apk + - /go/pkg/mod + stages: - test - review before_script: - - apk add --no-cache git build-base bash make - - mkdir -p /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds - - cp -r $CI_PROJECT_DIR /go/src/gitlab.com/$CI_PROJECT_PATH - - ln -s /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds/$CI_PROJECT_NAMESPACE + - apk add --no-cache git make - go get github.com/golangci/golangci-lint/cmd/golangci-lint - go install github.com/golangci/golangci-lint/cmd/golangci-lint - - make dep + - make tidy unit_tests: stage: test @@ -28,4 +36,4 @@ lint_code: stage: review script: - make lint - allow_failure: true + allow_failure: true \ No newline at end of file diff --git a/Makefile b/Makefile index 24bd0f9..0feac98 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,12 @@ -PROJECT_NAMESPACE := $(CI_PROJECT_NAMESPACE) PROJECT_NAME := $(CI_PROJECT_NAME) +PROJECT_NAMESPACE := $(CI_PROJECT_NAMESPACE) PROJECT_PATH := "$(PROJECT_NAMESPACE)/$(PROJECT_NAME)" PACKAGE_NAME := "gitlab.com/$(PROJECT_PATH)" -PACKAGE_PATH := "$(GOPATH)/src/$(PACKAGE_NAME)" PACKAGE_LIST := $(shell go list $(PACKAGE_NAME)/... | grep -v /vendor/) -GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go) -.PHONY: all lint test rase coverage dep +.PHONY: all lint test race coverage tidy -all: dep test race lint +all: tidy test race lint lint: ## Lint the files @golangci-lint run ./... @@ -16,14 +14,14 @@ lint: ## Lint the files test: ## Run unittests @go test -short $(PACKAGE_NAME)/... -race: dep ## Run data race detector +race: tidy ## Run data race detector @go test -race -short ${PACKAGE_LIST} coverage: ## Generate global code coverage report @go test -cover -v -coverpkg=$(PACKAGE_NAME)/... ${PACKAGE_LIST} -dep: ## Get the dependencies - @go get -v -d -t $(PACKAGE_NAME)/... +tidy: ## Get the dependencies + @go mod tidy help: ## Display this help screen - @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' \ No newline at end of file diff --git a/content.go b/content.go index d7e4fe4..3c81526 100644 --- a/content.go +++ b/content.go @@ -8,10 +8,10 @@ import ( "golang.org/x/net/html" ) -// ContentFormat transforms data to a DOM-based format to represent the content -// of the page. +// ContentFormat transforms data to a DOM-based format to represent the content of the page. func ContentFormat(data interface{}) (n []Node, err error) { - dst := new(html.Node) + var dst *html.Node + switch src := data.(type) { case string: dst, err = html.Parse(strings.NewReader(src)) @@ -20,14 +20,16 @@ func ContentFormat(data interface{}) (n []Node, err error) { case io.Reader: dst, err = html.Parse(src) default: - err = ErrInvalidDataType + return nil, ErrInvalidDataType } + if err != nil { return nil, err } n = append(n, domToNode(dst.FirstChild)) - return + + return n, nil } func domToNode(domNode *html.Node) interface{} { @@ -40,18 +42,16 @@ func domToNode(domNode *html.Node) interface{} { } var nodeElement 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": + 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, - } + nodeElement.Attrs = map[string]string{domNode.Attr[i].Key: domNode.Attr[i].Val} default: continue } diff --git a/create_account.go b/create_account.go index e550a83..69cbc71 100644 --- a/create_account.go +++ b/create_account.go @@ -1,27 +1,35 @@ package telegraph -import ( - http "github.com/valyala/fasthttp" -) +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"` -// 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. + // Default author name used when creating new articles. + AuthorName string `json:"author_name,omitempty"` + + // 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"` +} + +// 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) { - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("short_name", account.ShortName) // required - args.Add("author_name", account.AuthorName) - args.Add("author_url", account.AuthorURL) - - data, err := makeRequest("createAccount", args) + data, err := makeRequest("createAccount", createAccount{ + ShortName: account.ShortName, + AuthorName: account.AuthorName, + AuthorURL: account.AuthorURL, + }) if err != nil { return nil, err } - var result Account - err = parser.Unmarshal(data, &result) - return &result, err + result := new(Account) + if err = parser.Unmarshal(data, result); err != nil { + return nil, err + } + + return result, nil } diff --git a/create_page.go b/create_page.go index 89382c6..8aadc96 100644 --- a/create_page.go +++ b/create_page.go @@ -1,33 +1,44 @@ package telegraph -import ( - "strconv" +type createPage struct { + // Access token of the Telegraph account. + AccessToken string `json:"access_token"` - http "github.com/valyala/fasthttp" -) + // Page title. + Title string `json:"title"` + + // Author name, displayed below the article's title. + AuthorName string `json:"author_name,omitempty"` + + // 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"` +} // CreatePage create a new Telegraph page. On success, returns a Page object. func (a *Account) CreatePage(page Page, returnContent bool) (*Page, error) { - src, err := parser.Marshal(page.Content) + 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, err } - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("access_token", a.AccessToken) // required - args.Add("title", page.Title) // required - args.Add("author_name", page.AuthorName) - args.Add("author_url", page.AuthorURL) - args.AddBytesV("content", src) - args.Add("return_content", strconv.FormatBool(returnContent)) - - data, err := makeRequest("createPage", args) - if err != nil { + result := new(Page) + if err = parser.Unmarshal(data, &result); err != nil { return nil, err } - var result Page - err = parser.Unmarshal(data, &result) - return &result, err + return result, nil } diff --git a/doc.go b/doc.go index 476ac3d..c83eb01 100644 --- a/doc.go +++ b/doc.go @@ -1,15 +1,14 @@ /* 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. +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. +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. +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 "gitlab.com/toby3d/telegraph" \ No newline at end of file +package telegraph // import "gitlab.com/toby3d/telegraph" diff --git a/edit_account_info.go b/edit_account_info.go index 0356fea..bba2f23 100644 --- a/edit_account_info.go +++ b/edit_account_info.go @@ -1,26 +1,37 @@ package telegraph -import ( - http "github.com/valyala/fasthttp" -) +type editAccountInfo struct { + // Access token of the Telegraph account. + AccessToken string `json:"access_token"` -// 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. + // New account name. + ShortName string `json:"short_name,omitempty"` + + // New default author name used when creating new articles. + AuthorName string `json:"author_name,omitempty"` + + // 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"` +} + +// 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) { - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("access_token", a.AccessToken) // required - args.Add("short_name", update.ShortName) - args.Add("author_name", update.AuthorName) - args.Add("author_url", update.AuthorURL) - - data, err := makeRequest("editAccountInfo", args) + data, err := makeRequest("editAccountInfo", editAccountInfo{ + AccessToken: a.AccessToken, + ShortName: update.ShortName, + AuthorName: update.AuthorName, + AuthorURL: update.AuthorURL, + }) if err != nil { return nil, err } - var result Account - err = parser.Unmarshal(data, &result) - return &result, err + result := new(Account) + if err = parser.Unmarshal(data, result); err != nil { + return nil, err + } + + return result, nil } diff --git a/edit_page.go b/edit_page.go index 14065f7..e414344 100644 --- a/edit_page.go +++ b/edit_page.go @@ -2,34 +2,51 @@ package telegraph import ( "path" - "strconv" - - http "github.com/valyala/fasthttp" ) +type editPage struct { + // Access token of the Telegraph account. + AccessToken string `json:"access_token"` + + // Path to the page. + Path string `json:"path"` + + // Page title. + Title string `json:"title"` + + // Content of the page. + Content []Node `json:"content"` + + // Author name, displayed below the article's title. + AuthorName string `json:"author_name,omitempty"` + + // 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"` +} + // EditPage edit an existing Telegraph page. On success, returns a Page object. func (a *Account) EditPage(update Page, returnContent bool) (*Page, error) { - src, err := parser.Marshal(update.Content) + 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, err } - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("access_token", a.AccessToken) // required - args.Add("path", update.Path) // required - args.Add("title", update.Title) // required - args.AddBytesV("content", src) // required - args.Add("author_name", update.AuthorName) - args.Add("author_url", update.AuthorURL) - args.Add("return_content", strconv.FormatBool(returnContent)) - - data, err := makeRequest(path.Join("editPage", update.Path), args) - if err != nil { + result := new(Page) + if err = parser.Unmarshal(data, result); err != nil { return nil, err } - var result Page - err = parser.Unmarshal(data, &result) - return &result, err + return result, nil } diff --git a/example_test.go b/example_test.go index e55ac0b..e373c40 100644 --- a/example_test.go +++ b/example_test.go @@ -2,6 +2,7 @@ package telegraph_test import ( "log" + "time" "gitlab.com/toby3d/telegraph" ) @@ -160,6 +161,7 @@ func ExampleAccount_GetPageList() { 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) @@ -168,7 +170,8 @@ func ExampleAccount_GetPageList() { func ExampleGetViews() { pagePath := "Sample-Page-12-15" - views, err := telegraph.GetViews(pagePath, 2016, 12) + 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") diff --git a/get_account_info.go b/get_account_info.go index ba8cc1f..b11980b 100644 --- a/get_account_info.go +++ b/get_account_info.go @@ -1,26 +1,27 @@ package telegraph -import ( - "strings" +type getAccountInfo struct { + // Access token of the Telegraph account. + AccessToken string `json:"access_token"` - http "github.com/valyala/fasthttp" -) + // List of account fields to return. + Fields []string `json:"fields,omitempty"` +} // GetAccountInfo get information about a Telegraph account. Returns an Account object on success. func (a *Account) GetAccountInfo(fields ...string) (*Account, error) { - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("access_token", a.AccessToken) // required - if len(fields) > 0 { - args.Add("fields", `["`+strings.Join(fields, `","`)+`"]`) - } - - data, err := makeRequest("getAccountInfo", args) + data, err := makeRequest("getAccountInfo", getAccountInfo{ + AccessToken: a.AccessToken, + Fields: fields, + }) if err != nil { return nil, err } - var result Account - err = parser.Unmarshal(data, &result) - return &result, err + result := new(Account) + if err = parser.Unmarshal(data, result); err != nil { + return nil, err + } + + return result, nil } diff --git a/get_page.go b/get_page.go index fad4420..1437036 100644 --- a/get_page.go +++ b/get_page.go @@ -2,24 +2,30 @@ package telegraph import ( gopath "path" - "strconv" - - http "github.com/valyala/fasthttp" ) +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. + ReturnContent bool `json:"return_content,omitempty"` +} + // GetPage get a Telegraph page. Returns a Page object on success. func GetPage(path string, returnContent bool) (*Page, error) { - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("path", path) // required - args.Add("return_content", strconv.FormatBool(returnContent)) - - data, err := makeRequest(gopath.Join("getPage", path), args) + data, err := makeRequest(gopath.Join("getPage", path), getPage{ + Path: path, + ReturnContent: returnContent, + }) if err != nil { return nil, err } - var result Page - err = parser.Unmarshal(data, &result) - return &result, err + result := new(Page) + if err = parser.Unmarshal(data, result); err != nil { + return nil, err + } + + return result, nil } diff --git a/get_page_list.go b/get_page_list.go index 5a90dab..760fc48 100644 --- a/get_page_list.go +++ b/get_page_list.go @@ -1,30 +1,32 @@ package telegraph -import ( - "strconv" +type getPageList struct { + // Access token of the Telegraph account. + AccessToken string `json:"access_token"` - http "github.com/valyala/fasthttp" -) + // Sequential number of the first page to be returned. + Offset int `json:"offset,omitempty"` -// GetPageList get a list of pages belonging to a Telegraph account. Returns a PageList object, sorted -// by most recently created pages first. + // Limits the number of pages to be retrieved. + Limit int `json:"limit,omitempty"` +} + +// 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) { - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("access_token", a.AccessToken) // required - if offset > 0 { - args.Add("offset", strconv.Itoa(offset)) - } - if limit > 0 { - args.Add("limit", strconv.Itoa(limit)) - } - - data, err := makeRequest("getPageList", args) + data, err := makeRequest("getPageList", getPageList{ + AccessToken: a.AccessToken, + Offset: offset, + Limit: limit, + }) if err != nil { return nil, err } - var result PageList - err = parser.Unmarshal(data, &result) - return &result, err + result := new(PageList) + if err = parser.Unmarshal(data, result); err != nil { + return nil, err + } + + return result, nil } diff --git a/get_views.go b/get_views.go index f5c2fa5..984ecd1 100644 --- a/get_views.go +++ b/get_views.go @@ -2,36 +2,49 @@ package telegraph import ( gopath "path" - "strconv" - - http "github.com/valyala/fasthttp" + "time" ) -// 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 ...int) (*PageViews, error) { - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("path", path) // required - if len(date) > 0 { - args.Add("year", strconv.Itoa(date[0])) - } - if len(date) > 1 { - args.Add("month", strconv.Itoa(date[1])) - } - if len(date) > 2 { - args.Add("day", strconv.Itoa(date[2])) - } - if len(date) > 3 { - args.Add("hour", strconv.Itoa(date[3])) +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 int `json:"year,omitempty"` + + // 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 int `json:"day,omitempty"` + + // If passed, the number of page views for the requested hour will be returned. + Hour int `json:"hour,omitempty"` +} + +// 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 + + if !date.IsZero() { + p.Year = date.Year() + p.Month = int(date.Month()) + p.Day = date.Day() + p.Hour = date.Hour() } - data, err := makeRequest(gopath.Join("getViews", path), args) + data, err := makeRequest(gopath.Join("getViews", path), p) if err != nil { return nil, err } - var result PageViews - err = parser.Unmarshal(data, &result) - return &result, err + result := new(PageViews) + if err = parser.Unmarshal(data, &result); err != nil { + return nil, err + } + + return result, nil } diff --git a/get_views_test.go b/get_views_test.go index 7167407..65dec11 100644 --- a/get_views_test.go +++ b/get_views_test.go @@ -2,6 +2,7 @@ package telegraph import ( "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -9,32 +10,41 @@ import ( func TestGetViews(t *testing.T) { t.Run("invalid", func(t *testing.T) { t.Run("path", func(t *testing.T) { - _, err := GetViews("wtf") + _, err := GetViews("wtf", time.Time{}) assert.Error(t, err) }) t.Run("year", func(t *testing.T) { - _, err := GetViews("Sample-Page-12-15", 1980) + dt := time.Date(1980, 0, 0, 0, 0, 0, 0, time.UTC) + _, err := GetViews("Sample-Page-12-15", dt) assert.Error(t, err) }) t.Run("month", func(t *testing.T) { - _, err := GetViews("Sample-Page-12-15", 2000, 22) - assert.Error(t, err) + dt := time.Date(2000, 22, 0, 0, 0, 0, 0, time.UTC) + result, err := GetViews("Sample-Page-12-15", dt) + assert.NoError(t, err) + assert.NotNil(t, result) }) t.Run("day", func(t *testing.T) { - _, err := GetViews("Sample-Page-12-15", 2000, 2, 42) - assert.Error(t, err) + dt := time.Date(2000, time.February, 42, 0, 0, 0, 0, time.UTC) + result, err := GetViews("Sample-Page-12-15", dt) + assert.NoError(t, err) + assert.NotNil(t, result) }) t.Run("hour", func(t *testing.T) { - _, err := GetViews("Sample-Page-12-15", 2000, 2, 12, 65) - assert.Error(t, err) + dt := time.Date(2000, time.February, 12, 65, 0, 0, 0, time.UTC) + result, err := GetViews("Sample-Page-12-15", dt) + assert.NoError(t, err) + assert.NotNil(t, result) }) }) t.Run("valid", func(t *testing.T) { - stats, err := GetViews("Sample-Page-12-15") + dt := time.Date(2016, time.December, 31, 0, 0, 0, 0, time.UTC) + stats, err := GetViews("Sample-Page-12-15", dt) assert.NoError(t, err) if !assert.NotNil(t, stats) { t.FailNow() } + assert.NotZero(t, stats.Views) }) } diff --git a/go.mod b/go.mod index 9a86d19..912249f 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,13 @@ module gitlab.com/toby3d/telegraph require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/json-iterator/go v1.1.6 - github.com/klauspost/compress v1.7.4 // indirect - github.com/klauspost/cpuid v1.2.1 // indirect + github.com/json-iterator/go v1.1.9 + github.com/klauspost/compress v1.9.7 // 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.4.0 - golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 + github.com/valyala/fasthttp v1.8.0 + golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 ) + +go 1.13 diff --git a/go.sum b/go.sum index c3a9164..df6cbbc 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,18 @@ 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/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/klauspost/compress v1.4.0 h1:8nsMz3tWa9SWWPL60G1V6CUsf4lLjWLTNEtibhe8gh8= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.7.4 h1:4UqAIzZ1Ns2epCTyJ1d2xMWvxtX+FNSCYWeOFogK9nc= -github.com/klauspost/compress v1.7.4/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e h1:+lIPJOWl+jSiJOc70QXJ07+2eg2Jy2EC7Mi11BWujeM= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7 h1:hYW1gP94JUmAhBtJ+LNz5My+gBobDxPR1iVuKug26aA= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +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= @@ -22,12 +22,12 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 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.4.0 h1:PuaTGZIw3mjYhhhbVbCQp8aciRZN9YdoB7MGX9Ko76A= -github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= +github.com/valyala/fasthttp v1.8.0 h1:actnGGBYtGQmxVaZxyZpp57Vcc2NhcO7mMN0IMwCC0w= +github.com/valyala/fasthttp v1.8.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= 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-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/revoke_access_token.go b/revoke_access_token.go index 27fbd4c..5c2e891 100644 --- a/revoke_access_token.go +++ b/revoke_access_token.go @@ -1,23 +1,25 @@ package telegraph -import ( - http "github.com/valyala/fasthttp" -) +type revokeAccessToken struct { + // Access token of the Telegraph account. + AccessToken string `json:"access_token"` +} // RevokeAccessToken revoke access_token and generate a new one, for example, if the user would // like to reset all connected sessions, or you have reasons to believe the token was compromised. On // success, returns an Account object with new access_token and auth_url fields. func (a *Account) RevokeAccessToken() (*Account, error) { - args := http.AcquireArgs() - defer http.ReleaseArgs(args) - args.Add("access_token", a.AccessToken) - - resp, err := makeRequest("revokeAccessToken", args) + resp, err := makeRequest("revokeAccessToken", revokeAccessToken{ + AccessToken: a.AccessToken, + }) if err != nil { return nil, err } - var account Account - err = parser.Unmarshal(resp, &account) - return &account, err + account := new(Account) + if err = parser.Unmarshal(resp, &account); err != nil { + return nil, err + } + + return account, nil } diff --git a/telegraph.go b/telegraph.go index 48619f1..ac0f25d 100644 --- a/telegraph.go +++ b/telegraph.go @@ -2,49 +2,53 @@ package telegraph import ( - gojson "encoding/json" + "encoding/json" "errors" - json "github.com/json-iterator/go" + jsoniter "github.com/json-iterator/go" http "github.com/valyala/fasthttp" ) -// Response contains a JSON object, which always has a Boolean field ok. If ok -// equals true, the request was successful, and the result of the query can be -// found in the result field. In case of an unsuccessful request, ok equals -// false, and the error is explained in the error field (e.g. -// SHORT_NAME_REQUIRED). +// Response contains a JSON object, which always has a Boolean field ok. If ok equals true, the request was +// successful, and the result of the query can be found in the result field. In case of an unsuccessful request, ok +// equals false, and the error is explained in the error field (e.g. SHORT_NAME_REQUIRED). type Response struct { - Ok bool `json:"ok"` - Error string `json:"error"` - Result gojson.RawMessage `json:"result,omitempty"` + Ok bool `json:"ok"` + Error string `json:"error,omitempty"` + Result json.RawMessage `json:"result,omitempty"` } -var parser = json.ConfigFastest //nolint:gochecknoglobals +var parser = jsoniter.ConfigFastest //nolint:gochecknoglobals + +func makeRequest(path string, payload interface{}) ([]byte, error) { + src, err := parser.Marshal(payload) + if err != nil { + return nil, err + } -func makeRequest(path string, args *http.Args) ([]byte, error) { u := http.AcquireURI() defer http.ReleaseURI(u) u.SetScheme("https") u.SetHost("api.telegra.ph") u.SetPath(path) - args.CopyTo(u.QueryArgs()) req := http.AcquireRequest() defer http.ReleaseRequest(req) - req.Header.SetMethod(http.MethodGet) req.SetRequestURIBytes(u.FullURI()) + req.Header.SetMethod(http.MethodPost) req.Header.SetUserAgent("toby3d/telegraph") - req.Header.SetContentType("application/json; charset=utf-8") + req.Header.SetContentType("application/json") + req.SetBody(src) resp := http.AcquireResponse() defer http.ReleaseResponse(resp) + if err := http.Do(req, resp); err != nil { return nil, err } - var r Response - if err := parser.Unmarshal(resp.Body(), &r); err != nil { + r := new(Response) + if err := parser.Unmarshal(resp.Body(), r); err != nil { return nil, err } diff --git a/types.go b/types.go index 97ea0e7..1691825 100644 --- a/types.go +++ b/types.go @@ -3,43 +3,38 @@ package telegraph import "errors" -// All types used in the Telegraph API responses are represented as JSON-objects. -// Optional fields may be not returned when irrelevant. +// All types used in the Telegraph API responses are represented as JSON-objects. Optional fields may be not returned +// when irrelevant. type ( // Account represents a Telegraph account. Account struct { - // Only returned by the createAccount and revokeAccessToken - // method. Access token of the Telegraph account. - AccessToken string `json:"access_token"` // optional + // Only returned by the createAccount and revokeAccessToken method. Access token of the Telegraph + // account. + AccessToken string `json:"access_token"` - // 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. - AuthURL string `json:"auth_url,omitempty"` // 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. + AuthURL string `json:"auth_url,omitempty"` - // 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. + // 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 string `json:"author_name"` - // 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. + // 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"` // Number of pages belonging to the Telegraph account. - PageCount int `json:"page_count,omitempty"` // optional + PageCount int `json:"page_count,omitempty"` } - // PageList represents a list of Telegraph articles belonging to an - // account. Most recently created articles first. + // PageList represents a list of Telegraph articles belonging to an account. Most recently created articles + // first. PageList struct { - // Total number of pages belonging to the target Telegraph - // account. + // Total number of pages belonging to the target Telegraph account. TotalCount int `json:"total_count"` // Requested pages of the target Telegraph account. @@ -61,25 +56,23 @@ type ( Description string `json:"description"` // Name of the author, displayed below the title. - AuthorName string `json:"author_name,omitempty"` // optional + AuthorName string `json:"author_name,omitempty"` - // 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"` // optional + // 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"` // Image URL of the page. - ImageURL string `json:"image_url,omitempty"` // optional + ImageURL string `json:"image_url,omitempty"` // Content of the page. - Content []Node `json:"content,omitempty"` // optional + Content []Node `json:"content,omitempty"` // Number of page views for the page. Views int `json:"views"` - // Only returned if access_token passed. True, if the target - // Telegraph account can edit the page. - CanEdit bool `json:"can_edit,omitempty"` // optional + // Only returned if access_token passed. True, if the target Telegraph account can edit the page. + CanEdit bool `json:"can_edit,omitempty"` } // PageViews represents the number of page views for a Telegraph article. @@ -88,50 +81,45 @@ type ( Views int `json:"views"` } - // Node is abstract object represents a DOM Node. It can be a String - // which represents a DOM text node or a NodeElement object. + // Node is abstract object represents a DOM Node. It can be a String which represents a DOM text node or a + // NodeElement object. Node interface{} // NodeElement represents a DOM element node. NodeElement struct { - // Name of the DOM element. - // Available tags: a, aside, b, blockquote, br, code, em, - // figcaption, figure, h3, h4, hr, i, iframe, img, li, ol, p, - // pre, s, strong, u, ul, video. + // Name of the DOM element. Available tags: a, aside, b, blockquote, br, code, em, figcaption, figure, + // h3, h4, hr, i, iframe, img, li, ol, p, pre, s, strong, u, ul, video. Tag string `json:"tag"` - // Attributes of the DOM element. Key of object represents name - // of attribute, value represents value of attribute. - // Available attributes: href, src. - Attrs map[string]string `json:"attrs,omitempty"` // optional + // Attributes of the DOM element. Key of object represents name of attribute, value represents value + // of attribute. Available attributes: href, src. + Attrs map[string]string `json:"attrs,omitempty"` // List of child nodes for the DOM element. - Children []Node `json:"children,omitempty"` // optional + Children []Node `json:"children,omitempty"` } ) const ( // FieldShortName used as GetAccountInfo argument for getting account name. - FieldShortName = "short_name" + FieldShortName string = "short_name" // FieldAuthorName used as GetAccountInfo argument for getting author name. - FieldAuthorName = "author_name" + FieldAuthorName string = "author_name" // FieldAuthorURL used as GetAccountInfo argument for getting profile link. - FieldAuthorURL = "author_url" + FieldAuthorURL string = "author_url" - // FieldAuthURL used as GetAccountInfo argument for getting URL to authorize - // a browser on telegra.ph. - FieldAuthURL = "auth_url" + // FieldAuthURL used as GetAccountInfo argument for getting URL to authorize a browser on telegra.ph. + FieldAuthURL string = "auth_url" - // FieldPageCount used as GetAccountInfo argument for getting number of pages - // belonging to the Telegraph account. - FieldPageCount = "page_count" + // FieldPageCount used as GetAccountInfo argument for getting number of pages belonging to the Telegraph + // account. + FieldPageCount string = "page_count" ) var ( - // ErrInvalidDataType is returned when ContentFormat function are passed - // a data argument of invalid type. + // ErrInvalidDataType is returned when ContentFormat function are passed a data argument of invalid type. ErrInvalidDataType = errors.New("invalid data type") // ErrNoInputData is returned when any method get nil argument.