commento/api/domain_import_disqus.go

208 lines
5.2 KiB
Go
Raw Normal View History

2018-06-14 16:58:11 +08:00
package main
import (
"compress/gzip"
"encoding/xml"
"github.com/lunny/html2md"
2018-06-16 21:30:03 +08:00
"io/ioutil"
"net/http"
"time"
2018-06-14 16:58:11 +08:00
)
type disqusThread struct {
XMLName xml.Name `xml:"thread"`
2018-06-16 21:30:03 +08:00
Id string `xml:"http://disqus.com/disqus-internals id,attr"`
URL string `xml:"link"`
Name string `xml:"name"`
2018-06-14 16:58:11 +08:00
}
type disqusAuthor struct {
2018-06-16 21:30:03 +08:00
XMLName xml.Name `xml:"author"`
IsAnonymous bool `xml:"isAnonymous"`
Name string `xml:"name"`
Email string `xml:"email"`
2018-06-14 16:58:11 +08:00
}
type disqusThreadId struct {
XMLName xml.Name `xml:"thread"`
Id string `xml:"http://disqus.com/disqus-internals id,attr"`
}
type disqusParentId struct {
XMLName xml.Name `xml:"parent"`
Id string `xml:"http://disqus.com/disqus-internals id,attr"`
}
type disqusPostId struct {
XMLName xml.Name `xml:"post"`
Id string `xml:"http://disqus.com/disqus-internals id,attr"`
}
type disqusPost struct {
2018-06-16 21:30:03 +08:00
XMLName xml.Name `xml:"post"`
Id string `xml:"http://disqus.com/disqus-internals id,attr"`
2018-06-14 16:58:11 +08:00
ThreadId disqusThreadId `xml:"thread"`
2018-06-16 21:30:03 +08:00
ParentId disqusParentId `xml:"parent"`
PostId disqusPostId `xml:"post"`
Message string `xml:"message"`
CreationDate time.Time `xml:"createdAt"`
IsDeleted bool `xml:"isDeleted"`
IsSpam bool `xml:"isSpam"`
Author disqusAuthor `xml:"author"`
2018-06-14 16:58:11 +08:00
}
type disqusXML struct {
2018-06-16 21:30:03 +08:00
XMLName xml.Name `xml:"disqus"`
2018-06-14 16:58:11 +08:00
Threads []disqusThread `xml:"thread"`
2018-06-16 21:30:03 +08:00
Posts []disqusPost `xml:"post"`
2018-06-14 16:58:11 +08:00
}
func domainImportDisqus(domain string, url string) (int, error) {
if domain == "" || url == "" {
return 0, errorMissingField
}
// TODO: make sure this is from disqus.com
resp, err := http.Get(url)
if err != nil {
logger.Errorf("cannot get url: %v", err)
return 0, errorCannotDownloadDisqus
}
defer resp.Body.Close()
zr, err := gzip.NewReader(resp.Body)
if err != nil {
logger.Errorf("cannot create gzip reader: %v", err)
return 0, errorInternal
}
contents, err := ioutil.ReadAll(zr)
if err != nil {
logger.Errorf("cannot read gzip contents uncompressed: %v", err)
return 0, errorInternal
}
x := disqusXML{}
err = xml.Unmarshal(contents, &x)
if err != nil {
logger.Errorf("cannot unmarshal XML: %v", err)
return 0, errorInternal
}
// Map Disqus thread IDs to threads.
threads := make(map[string]disqusThread)
for _, thread := range x.Threads {
threads[thread.Id] = thread
}
// Map Disqus emails to commenterHex (if not available, create a new one
// with a random password that can be reset later).
commenterHex := make(map[string]string)
for _, post := range x.Posts {
if post.IsDeleted || post.IsSpam {
continue
}
if _, ok := commenterHex[post.Author.Email]; ok {
continue
}
c, err := commenterGetByEmail("commento", post.Author.Email)
if err != nil && err != errorNoSuchCommenter {
logger.Errorf("cannot get commenter by email: %v", err)
return 0, errorInternal
}
if err == nil {
commenterHex[post.Author.Email] = c.CommenterHex
continue
}
randomPassword, err := randomHex(32)
if err != nil {
logger.Errorf("cannot generate random password for new commenter: %v", err)
return 0, errorInternal
}
commenterHex[post.Author.Email], err = commenterNew(post.Author.Email, post.Author.Name, "undefined", "undefined", "commento", randomPassword)
if err != nil {
return 0, err
}
}
// For each Disqus post, create a Commento comment. Attempt to convert the
// HTML to markdown.
numImported := 0
disqusIdMap := make(map[string]string)
for _, post := range x.Posts {
if post.IsDeleted || post.IsSpam {
continue
}
parentHex := "root"
if val, ok := disqusIdMap[post.ParentId.Id]; ok {
parentHex = val
}
// TODO: restrict the list of tags to just the basics: <a>, <b>, <i>, <code>
// Especially remove <img> (convert it to <a>).
commentHex, err := commentNew(
commenterHex[post.Author.Email],
domain,
2018-07-24 15:00:45 +08:00
pathStrip(threads[post.ThreadId.Id].URL),
2018-06-14 16:58:11 +08:00
parentHex,
html2md.Convert(post.Message),
"approved",
post.CreationDate)
if err != nil {
return numImported, err
}
disqusIdMap[post.PostId.Id] = commentHex
numImported += 1
}
return numImported, nil
}
func domainImportDisqusHandler(w http.ResponseWriter, r *http.Request) {
type request struct {
OwnerToken *string `json:"ownerToken"`
2018-06-20 11:50:11 +08:00
Domain *string `json:"domain"`
URL *string `json:"url"`
2018-06-14 16:58:11 +08:00
}
var x request
if err := bodyUnmarshal(r, &x); err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
2018-06-14 16:58:11 +08:00
return
}
o, err := ownerGetByOwnerToken(*x.OwnerToken)
2018-06-14 16:58:11 +08:00
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
2018-06-14 16:58:11 +08:00
return
}
2018-07-24 15:00:45 +08:00
domain := domainStrip(*x.Domain)
2018-06-14 16:58:11 +08:00
isOwner, err := domainOwnershipVerify(o.OwnerHex, domain)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
2018-06-14 16:58:11 +08:00
return
}
if !isOwner {
bodyMarshal(w, response{"success": false, "message": errorNotAuthorised.Error()})
2018-06-14 16:58:11 +08:00
return
}
numImported, err := domainImportDisqus(domain, *x.URL)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
2018-06-14 16:58:11 +08:00
return
}
bodyMarshal(w, response{"success": true, "numImported": numImported})
2018-06-14 16:58:11 +08:00
}