😎 Kaboom! All done and closes #1

All methods and types available! Yaay!
This commit is contained in:
Maxim Lebedev 2016-12-24 22:09:28 +05:00 committed by GitHub
parent a6f03fc370
commit 128023ad07
No known key found for this signature in database
GPG Key ID: F8978F46FF0FFA4F
7 changed files with 307 additions and 178 deletions

View File

@ -6,3 +6,4 @@ go:
install:
- go get
- go get golang.org/x/net/html

View File

@ -8,25 +8,7 @@
[![Patreon](https://img.shields.io/badge/support-patreon-E66500.svg?maxAge=2592000)](https://www.patreon.com/toby3d)
[![discord](https://discordapp.com/api/guilds/208605007744860163/widget.png)](https://discord.gg/fM4QqmA)
## Available features
### [Methods](http://telegra.ph/api#Available-methods)
- [x] createAccount
- [ ] createPage
- [x] editAccountInfo
- [ ] editPage
- [x] getAccountInfo
- [x] getPage
- [x] getPageList
- [x] getViews
- [x] revokeAccessToken
### [Types](http://telegra.ph/api#Available-types)
- [x] Account
- [ ] Node
- [ ] NodeElement
- [x] Page
- [x] PageList
- [x] PageViews
All [methods](http://telegra.ph/api#Available-methods) and [types](http://telegra.ph/api#Available-types) available! Yaay!
## Start using telegraph
Download and install it:
@ -35,6 +17,53 @@ Download and install it:
Import it in your code:
`import "github.com/toby3d/telegraph"`
## Example
This is an example of "quick start", which shows **how to create a new account** for future pages, as well as **creating a first simple page** with the name, picture and signature:
```go
package main
import (
"github.com/toby3d/telegraph"
"log"
)
// Example content. Not abuse tags, okay? Be easy, bro.
const data = `<figure><img src="http://telegra.ph/file/6a5b15e7eb4d7329ca7af.jpg"/>
<figcaption>Cat turns the wheel? Pretty weird... But cute.</figcaption></figure>
<p><i>Hello</i>, my name is <b>Page</b>, <u>look at me</u>!</p>`
func main() {
// Create new Telegraph account. Author name/link can be epmty.
// So secure. Much anonymously. Wow.
acc, err := telegraph.CreateAccount(
"toby3d", // required for assign all new pages (invisible for others)
"Maxim Lebedev",
"https://telegram.me/toby3d",
)
if err != nil {
log.Fatal(err.Error())
}
// Boom!.. And your text will be understandable for Telegraph. MAGIC.
content, _ := telegraph.ContentFormat(data)
newPage := &telegraph.Page{
Title: "My awesome page",
Content: content,
// Not necessarily, but, hey, it's just an example.
AuthorName: acc.AuthorName,
AuthorURL: acc.AuthorURL,
}
if page, err := acc.CreatePage(newPage, false); err != nil {
log.Print(err.Error())
} else {
log.Println("Kaboom! Page created, look what happened:", page.URL)
}
}
```
## Requirements
- [fasthttp](https://github.com/valyala/fasthttp)
- [net/html](https://golang.org/x/net/html)

View File

@ -7,29 +7,27 @@ import (
"strings"
)
// 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.
// 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(shortName string, authorName string, authorURL string) (*Account, error) {
var args fasthttp.Args
// Required. 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.
args.Add("short_name", shortName)
// Required. 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.
args.Add("author_name", authorName)
// Default author name used when creating new articles.
args.Add("author_name", authorName)
// 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.
args.Add("author_url", authorURL)
// 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.
url := fmt.Sprintf(APIEndpoint, "createAccount")
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}
@ -42,28 +40,26 @@ func CreateAccount(shortName string, authorName string, authorURL string) (*Acco
return &resp, nil
}
// 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.
// 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 (account *Account) EditAccountInfo(update *Account) (*Account, error) {
var args fasthttp.Args
args.Add("access_token", account.AccessToken)
// Required. Access token of the Telegraph account.
args.Add("access_token", account.AccessToken)
args.Add("short_name", update.ShortName)
// New account name.
args.Add("short_name", update.ShortName)
args.Add("author_name", update.AuthorName)
// New default author name used when creating new articles.
args.Add("author_name", update.AuthorName)
// 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.
args.Add("author_url", update.AuthorURL)
// 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.
url := fmt.Sprintf(APIEndpoint, "editAccountInfo")
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}
@ -76,21 +72,19 @@ func (account *Account) EditAccountInfo(update *Account) (*Account, error) {
return &resp, nil
}
// GetAccountInfo get information about a Telegraph account.
// Returns an Account object on success.
// GetAccountInfo get information about a Telegraph account. Returns an Account object on success.
func (account *Account) GetAccountInfo(fields []string) (*Account, error) {
var args fasthttp.Args
args.Add("access_token", account.AccessToken)
// Required. Access token of the Telegraph account.
args.Add("access_token", account.AccessToken)
args.Add("fields", fmt.Sprintf("[\"%s\"]", strings.Join(fields, "\",\"")))
// List of account fields to return.
// Available fields: short_name, author_name, author_url, auth_url,
// page_count.
// List of account fields to return. Available fields: short_name, author_name, author_url,
// auth_url, page_count.
args.Add("fields", fmt.Sprintf(`["%s"]`, strings.Join(fields, `","`)))
url := fmt.Sprintf(APIEndpoint, "getAccountInfo")
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}
@ -103,18 +97,17 @@ func (account *Account) GetAccountInfo(fields []string) (*Account, error) {
return &resp, nil
}
// 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.
// 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 (account *Account) RevokeAccessToken() (*Account, error) {
var args fasthttp.Args
args.Add("access_token", account.AccessToken)
// Required. Access token of the Telegraph account.
args.Add("access_token", account.AccessToken)
url := fmt.Sprintf(APIEndpoint, "revokeAccessToken")
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}

95
content.go Normal file
View File

@ -0,0 +1,95 @@
package telegraph
import (
"bytes"
"errors"
"golang.org/x/net/html"
"strings"
)
var availableTags = map[string]bool{
"a": true,
"aside": true,
"b": true,
"blockquote": true,
"br": true,
"code": true,
"em": true,
"figcaption": true,
"figure": true,
"h3": true,
"h4": true,
"hr": true,
"i": true,
"iframe": true,
"img": true,
"li": true,
"ol": true,
"p": true,
"pre": true,
"s": true,
"strong": true,
"u": true,
"ul": true,
"video": true,
}
var availableAttributes = map[string]bool{
"href": true,
"src": true,
}
// ContentFormat transforms data to a DOM-based format to represent the content of the page.
func ContentFormat(data interface{}) ([]Node, error) {
var doc html.Node
switch dst := data.(type) {
case string:
dom, err := html.Parse(strings.NewReader(dst))
if err != nil {
return nil, err
}
doc = *dom
case []byte:
dom, err := html.Parse(bytes.NewReader(dst))
if err != nil {
return nil, err
}
doc = *dom
default:
return nil, errors.New("invalid data type, use []byte or string")
}
var content []Node
content = append(content, domToNode(doc.FirstChild))
return content, nil
}
func domToNode(domNode *html.Node) interface{} {
if domNode.Type == html.TextNode {
return domNode.Data
}
if domNode.Type != html.ElementNode {
return nil
}
var nodeElement NodeElement
if _, ok := availableTags[strings.ToLower(domNode.Data)]; ok {
nodeElement.Tag = strings.ToLower(domNode.Data)
for i := range domNode.Attr {
attr := domNode.Attr[i]
if _, ok := availableAttributes[strings.ToLower(attr.Key)]; ok {
nodeElement.Attrs = map[string]string{
strings.ToLower(attr.Key): strings.ToLower(attr.Val),
}
}
}
}
for child := domNode.FirstChild; child != nil; child = child.NextSibling {
nodeElement.Children = append(nodeElement.Children, domToNode(child))
}
return nodeElement
}

136
page.go
View File

@ -7,32 +7,40 @@ import (
"strconv"
)
/*
// CreatePage create a new Telegraph page. On success, returns a Page object.
func (account *Account) CreatePage(page *Page, returnContent bool) (*Page, error) {
var args fasthttp.Args
args.Add("access_token", account.AccessToken)
// Required. Access token of the Telegraph account.
args.Add("access_token", account.AccessToken)
args.Add("title", page.Title)
// Required. Page title.
args.Add("title", page.Title)
args.Add("author_name", page.AuthorName)
// Author name, displayed below the article's title.
if page.AuthorName != "" {
// Author name, displayed below the article's title.
args.Add("author_name", page.AuthorName)
}
args.Add("author_url", page.AuthorURL)
// 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.
if page.AuthorURL != "" {
// 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.
args.Add("author_url", page.AuthorURL)
}
args.Add("content", page.Content)
// Required. Content of the page.
args.Add("return_content", strconv.FormatBool(returnContent))
// If true, a content field will be returned in the Page object.
args.Add("return_content", strconv.FormatBool(returnContent))
content, err := json.Marshal(page.Content)
if err != nil {
return nil, err
}
// Required. Content of the page.
args.Add("content", string(content))
url := fmt.Sprintf(APIEndpoint, "createPage")
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}
@ -44,34 +52,41 @@ func (account *Account) CreatePage(page *Page, returnContent bool) (*Page, error
return &resp, nil
}
*/
/*
// EditPage edit an existing Telegraph page. On success, returns a Page object.
func (account *Account) EditPage(update *Page, returnContent bool) (*Page, error) {
func (account *Account) EditPage(page *Page, returnContent bool) (*Page, error) {
var args fasthttp.Args
args.Add("access_token", account.AccessToken)
// Required. Access token of the Telegraph account.
args.Add("access_token", account.AccessToken)
args.Add("title", update.Title)
// Required. Page title.
args.Add("title", page.Title)
args.Add("content", update.Content.Data)
// Required. Content of the page.
if page.AuthorName != "" {
// Author name, displayed below the article's title.
args.Add("author_name", page.AuthorName)
}
args.Add("author_name", update.AuthorName)
// Author name, displayed below the article's title.
if page.AuthorURL != "" {
// 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.
args.Add("author_url", page.AuthorURL)
}
args.Add("author_url", update.AuthorURL)
// 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.
args.Add("return_content", strconv.FormatBool(returnContent))
// If true, a content field will be returned in the Page object.
args.Add("return_content", strconv.FormatBool(returnContent))
url := fmt.Sprintf(PathEndpoint, "editPage", update.Path)
body, err := request(nil, url, &args)
content, err := json.Marshal(page.Content)
if err != nil {
return nil, err
}
// Required. Content of the page.
args.Add("content", string(content))
url := fmt.Sprintf(PathEndpoint, "editPage", page.Path)
body, err := request(url, &args)
if err != nil {
return nil, err
}
@ -83,17 +98,16 @@ func (account *Account) EditPage(update *Page, returnContent bool) (*Page, error
return &resp, nil
}
*/
// GetPage get a Telegraph page. Returns a Page object on success.
func GetPage(path string, returnContent bool) (*Page, error) {
var args fasthttp.Args
args.Add("return_content", strconv.FormatBool(returnContent))
// If true, content field will be returned in Page object.
args.Add("return_content", strconv.FormatBool(returnContent))
url := fmt.Sprintf(PathEndpoint, "getPage", path)
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}
@ -106,22 +120,22 @@ func GetPage(path string, returnContent bool) (*Page, error) {
return &resp, nil
}
// GetPageList get a list of pages belonging to a Telegraph account. Returns a
// PageList object, sorted by most recently created pages first.
// GetPageList get a list of pages belonging to a Telegraph account. Returns a PageList object,
// sorted by most recently created pages first.
func (account *Account) GetPageList(offset int, limit int) (*PageList, error) {
var args fasthttp.Args
args.Add("access_token", account.AccessToken)
// Required. Access token of the Telegraph account.
args.Add("access_token", account.AccessToken)
args.Add("offset", strconv.Itoa(offset))
// Sequential number of the first page to be returned.
args.Add("offset", strconv.Itoa(offset))
args.Add("limit", strconv.Itoa(limit))
// Limits the number of pages to be retrieved.
args.Add("limit", strconv.Itoa(limit))
url := fmt.Sprintf(APIEndpoint, "getPageList")
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}
@ -134,38 +148,36 @@ func (account *Account) GetPageList(offset int, limit int) (*PageList, error) {
return &resp, nil
}
// 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.
// 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, year int, month int, day int, hour int) (*PageViews, error) {
var args fasthttp.Args
if year >= 2000 && year <= 2100 {
args.Add("year", strconv.Itoa(year))
// Required if month is passed. If passed, the number of page views for
// the requested year will be returned.
}
if month > 0 {
args.Add("month", strconv.Itoa(month))
// Required if day is passed. If passed, the number of page views for
// the requested month will be returned.
}
if day > 0 {
args.Add("day", strconv.Itoa(day))
// Required if hour is passed. If passed, the number of page views for
// the requested day will be returned.
}
if hour > -1 {
// If passed, the number of page views for the requested hour will be returned.
args.Add("hour", strconv.Itoa(hour))
// If passed, the number of page views for the requested hour will be
// returned.
if day > 0 {
// Required if hour is passed. If passed, the number of page views for the requested day
// will be returned.
args.Add("day", strconv.Itoa(day))
if month > 0 {
// Required if day is passed. If passed, the number of page views for the requested month
// will be returned.
args.Add("month", strconv.Itoa(month))
if year >= 2000 && year <= 2100 {
// Required if month is passed. If passed, the number of page views for the requested year
// will be returned.
args.Add("year", strconv.Itoa(year))
}
}
}
}
url := fmt.Sprintf(PathEndpoint, "getViews", path)
body, err := request(nil, url, &args)
body, err := request(url, &args)
if err != nil {
return nil, err
}

View File

@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"github.com/valyala/fasthttp"
"golang.org/x/net/html"
)
// Telegraph constants
@ -82,7 +81,7 @@ type (
ImageURL string `json:"image_url"`
// Optional. Content of the page.
Content []NodeElement `json:"content"`
Content []Node `json:"content"`
// Number of page views for the page.
Views int `json:"views"`
@ -112,10 +111,10 @@ type (
// Optional. Attributes of the DOM element. Key of object represents
// name of attribute, value represents value of attribute. Available
// attributes: href, src.
Attrs []html.Attribute `json:"attrs"`
Attrs map[string]string `json:"attrs"`
// Optional. List of child nodes for the DOM element.
Children []NodeElement `json:"children"`
Children []Node `json:"children"`
}
// Response represents a response from the Telegram API with the result
@ -130,20 +129,20 @@ type (
}
)
func request(dst []byte, url string, args *fasthttp.Args) (*Response, error) {
_, body, err := fasthttp.Post(dst, url, args)
func request(url string, args *fasthttp.Args) (*Response, error) {
_, res, err := fasthttp.Post(nil, url, args)
if err != nil {
return nil, err
}
var resp Response
if err := json.Unmarshal(body, &resp); err != nil {
var tResp Response
if err := json.Unmarshal(res, &tResp); err != nil {
return nil, err
}
if !resp.Ok {
return nil, errors.New(resp.Error)
if !tResp.Ok {
return nil, errors.New(tResp.Error)
}
return &resp, nil
return &tResp, nil
}

View File

@ -1,41 +1,45 @@
package telegraph
import "testing"
import (
"testing"
)
var (
demoAccount = &Account{
AccessToken: "b968da509bb76866c35425099bc0989a5ec3b32997d55286c657e6994bbb",
}
demoPage = &Page{
Path: "Sample-Page-12-15",
}
demoContent = `<p>Hello, world!<p>`
demoAccount Account
demoPage Page
demoContent = `<p>Hello, World!</p>`
)
func TestCreateAccount(t *testing.T) {
newAccount, err := CreateAccount("Sandbox", "Anonymous", "")
acc, err := CreateAccount("Sandbox", "Anonymous", "")
if err != nil {
t.Error(err)
t.Error(err.Error())
}
t.Logf("New account created!\nAccess Token: %s\nAuth URL: %s\nShort Name: %s\nAuthor Name: %s\nPage Count: %d", newAccount.AccessToken, newAccount.AuthURL, newAccount.ShortName, newAccount.AuthorName, newAccount.PageCount)
demoAccount = *acc
t.Logf("New account created!\n%#v", *acc)
}
/*
func TestCreatePage(t *testing.T) {
content, err := ContentFormat(demoContent)
if err != nil {
t.Error(err.Error())
}
newPage := &Page{
Title: "Sample Page",
AuthorName: "Anonymous",
Content: demoContent,
Content: content,
}
demoPage, err = demoAccount.CreatePage(newPage, true)
page, err := demoAccount.CreatePage(newPage, true)
if err != nil {
t.Error(err)
t.Error(err.Error())
}
t.Logf("%#v", demoPage)
demoPage = *page
t.Logf("%#v", *page)
}
*/
func TestEditAccountInfo(t *testing.T) {
update := &Account{
@ -44,78 +48,74 @@ func TestEditAccountInfo(t *testing.T) {
}
info, err := demoAccount.EditAccountInfo(update)
if err != nil {
t.Error(err.Error())
}
t.Logf("Account updated!\n%#v", info)
}
func TestEditPage(t *testing.T) {
content, err := ContentFormat(demoContent)
if err != nil {
t.Error(err)
}
t.Logf("Account updated!\nNew Short Name: %s\nNew Author Name: %s", info.ShortName, info.AuthorName)
}
/*
func TestEditPage(t *testing.T) {
update := &Page{
Path: demoPage.Path,
Title: "",
Title: "Sample Page",
AuthorName: "Anonymous",
Content: demoContent,
Content: content,
}
page, err := demoAccount.EditPage(update, true)
if err != nil {
t.Error(err)
t.Error(err.Error())
}
t.Logf("%#v", page)
t.Logf("%#v", *page)
}
*/
func TestGetAccountInfo(t *testing.T) {
account, err := demoAccount.GetAccountInfo([]string{"short_name", "page_count"})
if err != nil {
t.Error(err)
t.Error(err.Error())
}
t.Logf("Account info:\nShort Name: %s\nPage Count: %d", account.ShortName, account.PageCount)
}
/*
func TestGetPageList(t *testing.T) {
pages, err := demoAccount.GetPageList(0, 3)
if err != nil {
t.Error(err.Error())
}
t.Logf("Total %d pages\n%#v", pages.TotalCount, pages.Pages)
}
func TestGetPage(t *testing.T) {
page, err := GetPage(demoPage.Path, true)
if err != nil {
t.Error(err)
t.Error(err.Error())
}
t.Logf("%#v", page)
}
*/
func TestGetPageList(t *testing.T) {
list, err := demoAccount.GetPageList(0, 3)
if err != nil {
t.Error(err)
}
t.Logf("Total %d pages\nPages Raw: %#v", list.TotalCount, list.Pages)
}
func TestGetViews(t *testing.T) {
views, err := GetViews(demoPage.Path, 2016, 12, 0, -1)
views, err := GetViews("Sample-Page-12-15", 2016, 12, 0, -1)
if err != nil {
t.Error(err)
t.Error(err.Error())
}
t.Logf("This page have %d views", views.Views)
}
func TestRevokeAccessToken(t *testing.T) {
account, err := CreateAccount("Sandbox", "Anonymous", "")
if err != nil {
t.Error(err)
}
t.Logf("Old Access Token: %s", demoAccount.AccessToken)
t.Logf("Old Access Token: %s", account.AccessToken)
token, err := account.RevokeAccessToken()
token, err := demoAccount.RevokeAccessToken()
if err != nil {
t.Error(token)
}