Files
eiblog/pkg/internal/disqus.go
2025-01-01 02:49:21 +08:00

347 lines
7.4 KiB
Go

// Package internal provides ...
package internal
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/eiblog/eiblog/pkg/config"
"github.com/eiblog/eiblog/pkg/model"
)
// disqus api
const (
apiPostsCount = "https://disqus.com/api/3.0/threads/set.json"
apiPostsList = "https://disqus.com/api/3.0/threads/listPostsThreaded"
apiPostCreate = "https://disqus.com/api/3.0/posts/create.json"
apiPostApprove = "https://disqus.com/api/3.0/posts/approve.json"
apiThreadCreate = "https://disqus.com/api/3.0/threads/create.json"
apiThreadDetails = "https://disqus.com/api/3.0/threads/details.json"
disqusAPIKey = "E8Uh5l5fHZ6gD8U3KycjAIAk46f68Zw7C6eW8WSjZvCLXebZ7p0r1yrYDrLilk2F"
)
func checkDisqusConfig() error {
if config.Conf.EiBlogApp.Disqus.ShortName != "" &&
config.Conf.EiBlogApp.Disqus.PublicKey != "" &&
config.Conf.EiBlogApp.Disqus.AccessToken != "" {
return nil
}
return errors.New("disqus: config incompleted")
}
// postsCountResp 评论数量响应
type postsCountResp struct {
Code int
Response []struct {
ID string
Posts int
Identifiers []string
}
}
// PostsCount 获取文章评论数量
func PostsCount(articles map[string]*model.Article) error {
if err := checkDisqusConfig(); err != nil {
return err
}
vals := url.Values{}
vals.Set("api_key", config.Conf.EiBlogApp.Disqus.PublicKey)
vals.Set("forum", config.Conf.EiBlogApp.Disqus.ShortName)
// batch get
var count, index int
for _, article := range articles {
if index < len(articles) && count < 50 {
count++
index++
vals.Add("thread:ident", "post-"+article.Slug)
continue
}
count = 0
resp, err := httpGet(apiPostsCount + "?" + vals.Encode())
if err != nil {
return err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
// check http status code
if resp.StatusCode != http.StatusOK {
return errors.New(string(b))
}
result := &postsCountResp{}
err = json.Unmarshal(b, result)
if err != nil {
return err
}
for _, v := range result.Response {
i := strings.Index(v.Identifiers[0], "-")
slug := v.Identifiers[0][i+1:]
if article := articles[slug]; article != nil {
article.Count = v.Posts
if article.Thread == "" && v.ID != "" {
article.Thread = v.ID
}
}
}
}
return nil
}
// PostsListResp 获取评论列表
type PostsListResp struct {
Cursor struct {
HasNext bool
Next string
}
Code int
Response []postDetail
}
type postDetail struct {
Parent int
ID string
CreatedAt string
Message string
IsDeleted bool
Author struct {
Name string
ProfileURL string
Avatar struct {
Cache string
}
}
Thread string
}
// PostsList 评论列表
func PostsList(article *model.Article, cursor string) (*PostsListResp, error) {
if err := checkDisqusConfig(); err != nil {
return nil, err
}
vals := url.Values{}
vals.Set("api_key", disqusAPIKey)
vals.Set("forum", config.Conf.EiBlogApp.Disqus.ShortName)
vals.Set("thread:ident", "post-"+article.Slug)
vals.Set("cursor", cursor)
vals.Set("order", "popular")
vals.Set("limit", "50")
resp, err := httpGet(apiPostsList + "?" + vals.Encode())
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New(string(b))
}
result := &PostsListResp{}
err = json.Unmarshal(b, result)
if err != nil {
return nil, err
}
return result, nil
}
// PostComment 评论
type PostComment struct {
Message string
Parent string
Thread string
AuthorEmail string
AuthorName string
IPAddress string
Identifier string
UserAgent string
}
// PostCreateResp create comments resp
type PostCreateResp struct {
Code int
Response postDetail
}
// PostCreate 评论文章
func PostCreate(pc *PostComment) (*PostCreateResp, error) {
if err := checkDisqusConfig(); err != nil {
return nil, err
}
vals := url.Values{}
vals.Set("api_key", disqusAPIKey)
vals.Set("message", pc.Message)
vals.Set("parent", pc.Parent)
vals.Set("thread", pc.Thread)
vals.Set("author_email", pc.AuthorEmail)
vals.Set("author_name", pc.AuthorName)
// vals.Set("state", "approved")
header := http.Header{"Referer": {"https://disqus.com"}}
resp, err := httpPostHeader(apiPostCreate, vals, header)
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New(string(b))
}
result := &PostCreateResp{}
err = json.Unmarshal(b, result)
if err != nil {
return nil, err
}
return result, nil
}
// approvedResp 批准评论通过
type approvedResp struct {
Code int
Response []struct {
ID string
}
}
// PostApprove 批准评论
func PostApprove(post string) error {
if err := checkDisqusConfig(); err != nil {
return err
}
vals := url.Values{}
vals.Set("api_key", config.Conf.EiBlogApp.Disqus.PublicKey)
vals.Set("access_token", config.Conf.EiBlogApp.Disqus.AccessToken)
vals.Set("post", post)
header := http.Header{"Referer": {"https://disqus.com"}}
resp, err := httpPostHeader(apiPostApprove, vals, header)
if err != nil {
return err
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(string(b))
}
result := &approvedResp{}
return json.Unmarshal(b, result)
}
// threadCreateResp 创建thread
type threadCreateResp struct {
Code int
Response struct {
ID string
}
}
// ThreadCreate 创建thread
func ThreadCreate(article *model.Article, btitle string) error {
if err := checkDisqusConfig(); err != nil {
return err
}
vals := url.Values{}
vals.Set("api_key", config.Conf.EiBlogApp.Disqus.PublicKey)
vals.Set("access_token", config.Conf.EiBlogApp.Disqus.AccessToken)
vals.Set("forum", config.Conf.EiBlogApp.Disqus.ShortName)
vals.Set("title", article.Title+" | "+btitle)
vals.Set("identifier", "post-"+article.Slug)
urlPath := fmt.Sprintf("https://%s/post/%s.html", config.Conf.EiBlogApp.Host, article.Slug)
vals.Set("url", urlPath)
resp, err := httpPost(apiThreadCreate, vals)
if err != nil {
return err
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(string(b))
}
result := &threadCreateResp{}
err = json.Unmarshal(b, result)
if err != nil {
return err
}
article.Thread = result.Response.ID
return nil
}
// threadDetailsResp thread info
type threadDetailsResp struct {
Code int
Response struct {
ID string
}
}
// ThreadDetails thread详细
func ThreadDetails(article *model.Article) error {
if err := checkDisqusConfig(); err != nil {
return err
}
vals := url.Values{}
vals.Set("api_key", config.Conf.EiBlogApp.Disqus.PublicKey)
vals.Set("access_token", config.Conf.EiBlogApp.Disqus.AccessToken)
vals.Set("forum", config.Conf.EiBlogApp.Disqus.ShortName)
vals.Set("thread:ident", "post-"+article.Slug)
resp, err := httpGet(apiThreadDetails + "?" + vals.Encode())
if err != nil {
return err
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(string(b))
}
result := &threadDetailsResp{}
err = json.Unmarshal(b, result)
if err != nil {
return err
}
article.Thread = result.Response.ID
return nil
}