前言:
前幾天的文章中 [Jekyll] 移除 Disqu 替換到 utteranc 來使用 github issue 作為文章評論 ,我有提到我將本來部落格中的 Disqus 評論有搬到 https://utteranc.es/ 的過程,但是在 Disqus 裡面還有接近兩百個留言 (共有 189 個留言,分別在 55 篇文章內) 怎麼辦?
秉持著自己打造自己用的小工具原則( –關在家裡太無聊– ),就寫出了這個小工具來使用,希望能幫助到一些人。 也希望有更多的人一起加入開發相關功能。
TL;DR
本篇文將要介紹以下一些的部分,除了介紹如何使用外。人和
- 如何導出 Disqus Comment
- 套件: Disqus to github issue Go
- 包括哪些功能
- Disqus XML Format 解析
- Github issue created in Go
- 結論
- 成果
- 參考文章
如何導出 Disqus Comment 到 Utteranc
- 到 Disqus 管理介面,去導出: http://disqus.com/admin/discussions/export/ 。
- 大概需要過一兩天才會收到導出的檔案(不定時)。
- 到 Utteranc 設定導入
- 修改 Github Page 設定。
可以參考文章 [Jekyll] 移除 Disqu 替換到 utteranc 來使用 github issue 作為文章評論
套件: Disqus to github issues Go
https://github.com/kkdai/disqus-to-github-issues-go
- 安裝套件方式:``go get github.com/kkdai/disqus-to-github-issues-go`
- 並且提供一個 CLI 給大家先用,大家可以安裝
go install github.com/kkdai/disqus-to-github-issues-go/cmd/import_disqus_cli
相關指令:
-f
: Import disqus exported xml file. (get from http://disqus.com/admin/discussions/export/)-u
: github user name, you want to use for post github issue.-r
: github repo name, you want to use for post github issue.-t
: github token, you can request your from https://github.com/settings/tokens.
使用指令範例:
./import_disqus_cli -f="EXPORTED_XML_FILE" -r="BLOG_REPO" -u="GITHUB_USER" -t="YOUR_TOKEN"
包括哪些功能
- 讀取從 disqus export 的 XML file. (可以參考格式範例在 github )
- 列出所有的 Comments
- 列出所有文章(標題,作者,時間)
- 導入到 github issues (使用 utteranc 格式)
接下來會針對幾個部分放上相關程式碼,希望讓大家分享如何寫。
Disqus XML Format 解析
// Disqus: Disqus comment export structure in go | |
type DisqusStruct struct { | |
XMLName xml.Name `xml:"disqus"` | |
Text string `xml:",chardata"` | |
Xmlns string `xml:"xmlns,attr"` | |
Dsq string `xml:"dsq,attr"` | |
Xsi string `xml:"xsi,attr"` | |
SchemaLocation string `xml:"schemaLocation,attr"` | |
Category CategoryStruct `xml:"category"` | |
Articles []Article `xml:"thread"` | |
Commments []Comment `xml:"post"` | |
} |
這是簡單版本的 XML Parser,大家可以收到 XML 之後將檔案丟到 XML to Go 就可以取得格式。
其中比較需要解釋的如下:
Articles
: (xml tag: thread) 這邊指的就是原本的文章。他跟留言是一對多的關係。(一篇文章內可以有個留言)Comments
: (xml tag: post) 這邊是流言,資料比 Article 還後面需要另外做出 Mapping Table 來鏈結。
func isCommentBelongArticle(c Comment, a Article) bool { | |
return c.ArticleLink.ID == a.AttrID | |
} |
Article
與 Comments
連接關係為: Comment.ArticleLink.ID
跟 Article.AttrID
。
如何作出準備 Import github issue 的資料
目前採取方式是根據在 Githib Issue 的 title 當作 key ,來做 map 。
如果以 path 來當 title , ` mapping := make(map[string] Issue)` 來管理,可以很快速的透過 Map 來尋找到相關的資訊。
d.impData = make(map[string]Issue) | |
for _, c := range d.GetAllComments() { | |
a := articleMap[c.ArticleLink.ID] | |
shortLink := getShortPath(a.GetArticleLink()) | |
if issue, exist := d.impData[shortLink]; !exist { | |
//not exist, insert new issue. | |
ii := Issue{ | |
ArticleTitle: a.Title, | |
ArticleLink: a.Link, | |
ShortLink: shortLink, | |
} | |
ii.AppendComment(c) | |
d.impData[shortLink] = ii | |
} else { | |
//Exist, append new comment and update issue. | |
issue.AppendComment(c) | |
d.impData[shortLink] = issue | |
} | |
} |
留言排序
其中,每一篇文章的留言是需要排序的。為了確保留言可以在年度順序下呈現。需要相關 Sort 的準備如下:
type ByCreateAt []IssueComment | |
func (a ByCreateAt) Len() int { return len(a) } | |
func (a ByCreateAt) Swap(i, j int) { a[i], a[j] = a[j], a[i] } | |
func (a ByCreateAt) Less(i, j int) bool { return a[i].CreatedAt.Before(a[j].CreatedAt) } | |
type IssueComment struct { | |
Author string | |
CreatedAt time.Time | |
Body string | |
} |
透過準備 Sort Interfaces 的方式來讓 []Comment
支援 Sort()
。
Github issue created in Go
關於發送 Github Issue 的部分,使用的是 Google 開源的 golang 套件。 https://github.com/google/go-github ,這邊記得要使用最新的版本(筆者時間最新版本是 v35)
import (
...
"github.com/google/go-github/v35/github"
"golang.org/x/oauth2"
)
相關程式碼如下:
//CreateIssue : | |
func (b *CommentClient) CreateIssue(i *Issue) error { | |
ctx := context.Background() | |
ts := oauth2.StaticTokenSource( | |
&oauth2.Token{AccessToken: b.Token}, | |
) | |
tc := oauth2.NewClient(ctx, ts) | |
client := github.NewClient(tc) | |
commentBody := fmt.Sprintf("# %s \n \n \n [%s](%s)", i.ArticleTitle, i.ArticleLink, i.ArticleLink) | |
input := &github.IssueRequest{ | |
Title: String(i.ShortLink), | |
Body: String(commentBody), | |
Assignee: String(""), | |
Labels: &[]string{}, //&tags, | |
} | |
var gIssue *github.Issue | |
var err error | |
gIssue, _, err = client.Issues.Create(ctx, b.User, b.Repo, input) | |
if err != nil { | |
fmt.Println("Issues.Create returned error: ", err, " retry after 2 seconds.") | |
time.Sleep(2 * time.Second) | |
//retry once | |
gIssue, _, err = client.Issues.Create(ctx, b.User, b.Repo, input) | |
if err != nil { | |
fmt.Println("Issues.Create returned error: ", err, " retry after 2 seconds.") | |
return err | |
} | |
} | |
///Sort it before use it. | |
i.SortComments() | |
var id int64 | |
for _, c := range i.Comments { | |
id = id + 1 | |
body := fmt.Sprintf("comment written by %s, created at %s, \n\n %s", c.Author, c.CreatedAt.Format(time.RFC822), c.Body) | |
cm := &github.IssueComment{ | |
ID: &(id), | |
Body: &body} | |
if _, res, err := client.Issues.CreateComment(ctx, b.User, b.Repo, *gIssue.Number, cm); err != nil { | |
log.Println("Create comment res", res, " error code:", err) | |
return err | |
} | |
//sleep 500 millisecond to avoid github limited. | |
time.Sleep(300 * time.Millisecond) | |
} | |
return nil | |
} |
其中 gIssue, _, err = client.Issues.Create(ctx, b.User, b.Repo, input)
後會取得建立好的 Github Issue 相關訊息,需要相關的 ID 來後續加入 Comment 。
這一段程式碼,其實對於許多開發者想使用 Github Issue 作為資料管理的方式可以參考(聽說有人拿 Github Issue 來當 DB www) 。
成果
最後執行過後,可以將 Disqus 的留言全部整合進 Github Issue 之中,並且可以保留相關時間。也可以在部落格裡面正常的呈現。希望這個工具能夠幫助大家。
結論:
本文分享了關於 Disqus 的輸出資料格式分析,並且分享了如何在 Github 上面建立 Issue 的相關程式碼。整合起來其實並不難,但是許多時候這些應用往往會變得很有趣。 大家可以參考筆者前一篇文章: [TIL] 為了自己的習慣,弄了一個簡單的服務 Github issue bookmark。