前言:

前幾天的文章中 [Jekyll] 移除 Disqu 替換到 utteranc 來使用 github issue 作為文章評論 ,我有提到我將本來部落格中的 Disqus 評論有搬到 https://utteranc.es/ 的過程,但是在 Disqus 裡面還有接近兩百個留言 (共有 189 個留言,分別在 55 篇文章內) 怎麼辦?

秉持著自己打造自己用的小工具原則( –關在家裡太無聊– ),就寫出了這個小工具來使用,希望能幫助到一些人。 也希望有更多的人一起加入開發相關功能。

TL;DR

本篇文將要介紹以下一些的部分,除了介紹如何使用外。人和

如何導出 Disqus Comment 到 Utteranc

可以參考文章 [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
}

ArticleComments 連接關係為: Comment.ArticleLink.IDArticle.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
}
}
view raw map_issue.go hosted with ❤ by GitHub

留言排序

其中,每一篇文章的留言是需要排序的。為了確保留言可以在年度順序下呈現。需要相關 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
}
view raw sort_comment.go hosted with ❤ by GitHub

透過準備 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
}
view raw github_issue.go hosted with ❤ by GitHub

其中 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

相關文章:


Buy Me A Coffee

Evan

Attitude is everything