-
-
Save neetsdkasu/7a5bdb72a3061697b8d564d0a1f2610e to your computer and use it in GitHub Desktop.
outbox.json解析 (mastodon)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.json | |
*.exe | |
*.txt | |
*.html | |
*.ico | |
*.mt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"fmt" | |
"io/ioutil" | |
"os" | |
"sort" | |
"strings" | |
"time" | |
) | |
func main() { | |
if err := run(); err != nil { | |
println(err.Error()) | |
} | |
} | |
func run() (err error) { | |
objects := []*Object_3_{} | |
files := []string{"f_outbox.json", "m_outbox.json", "p_outbox2.json"} | |
for _, file := range files { | |
var blob []byte | |
blob, err = ioutil.ReadFile(file) | |
if err != nil { | |
return | |
} | |
var o Object_1_ | |
if err = json.Unmarshal(blob, &o); err != nil { | |
return | |
} | |
fmt.Println(file+":", len(o.OrderedItems)) | |
objects = append(objects, o.OrderedItems...) | |
} | |
fmt.Println("total:", len(objects)) | |
sort.Slice(objects, func(i, j int) bool { | |
return strings.Compare(objects[i].Published, objects[j].Published) < 0 | |
}) | |
out, err := os.Create("mastodon.mt") | |
if err != nil { | |
return | |
} | |
defer func() { | |
tmp := out.Close() | |
if err == nil { | |
err = tmp | |
} | |
}() | |
ommit := func(obj *Object_4_) bool { | |
if strings.Contains(obj.InReplyTo, "drrr_sukima") { | |
return true | |
} | |
return strings.Contains(obj.Content, "ツイッターボタン女子絵のまとめ") | |
} | |
ids := ObjectMap{} | |
for _, item := range objects { | |
obj, ok := item.Object.GetObject_4_() | |
if !ok { | |
continue | |
} | |
if ommit(obj) { | |
continue | |
} | |
ids[obj.Id] = obj | |
} | |
j := 0 | |
for i, item := range objects[:0] { | |
obj, ok := item.Object.GetObject_4_() | |
if !ok { | |
continue | |
} | |
if ommit(obj) { | |
continue | |
} | |
fmt.Fprintln(out) | |
fmt.Fprintln(out, "[", j, "/", i, "]", "-------------------------") | |
// fmt.Fprintln(out, "Id:", item.Id) | |
// fmt.Fprintln(out, "Published:", item.Published) | |
// fmt.Fprintln(out, "Actor:", item.Actor) | |
// fmt.Fprintln(out, "To:", item.To) | |
// fmt.Fprintln(out, "Cc:", item.Cc) | |
// fmt.Fprintln(out, "--------------") | |
fmt.Fprintln(out, "Id:", obj.Id) | |
fmt.Fprintln(out, "Published:", obj.Published) | |
fmt.Fprintln(out, "Content:", obj.Content) | |
fmt.Fprintln(out, "Attachment:", len(obj.Attachment)) | |
fmt.Fprintln(out, "To:", obj.To) | |
fmt.Fprintln(out, "Cc:", obj.Cc) | |
fmt.Fprintln(out, "InReplyTo:", obj.InReplyTo) | |
if len(obj.InReplyTo) > 0 { | |
_, ok := ids[obj.InReplyTo] | |
fmt.Fprintln(out, ok) | |
} | |
fmt.Fprintln(out, "Url:", obj.Url) | |
j++ | |
content := findAnchor(obj.Content, func(url string) (int, string) { | |
fmt.Fprintln(out, url) | |
if !isMastodonURL(url) { | |
return 1, "" | |
} | |
switch { | |
case strings.Contains(url, "/media/"): | |
return -1, "" | |
case strings.Contains(url, "/tags/"): | |
return 0, "" | |
default: | |
return 1, "" | |
} | |
}) | |
if strings.Compare(obj.Content, content) != 0 { | |
fmt.Fprintln(out, "before:", obj.Content) | |
fmt.Fprintln(out, "after :", content) | |
} | |
} | |
currentDate := 0 | |
posts := []*Object_4_{} | |
for _, item := range objects[:0] { | |
obj, ok := item.Object.GetObject_4_() | |
if !ok || ommit(obj) { | |
continue | |
} | |
t := Toot{obj}.Time() | |
date := t.Year()*10000 + int(t.Month())*100 + t.Day() | |
if date == currentDate { | |
posts = append(posts, obj) | |
} else { | |
// if len(posts) > 20 && currentDate > 20170506 { | |
if currentDate == 20180417 { | |
break | |
} | |
posts = posts[:0] | |
posts = append(posts, obj) | |
currentDate = date | |
} | |
} | |
// fmt.Println(currentDate) | |
// dt := fmt.Sprintf("<p>%d</p>", currentDate) | |
// ss := makeList(ids, posts) | |
// sss := GetTestMT(0, dt, ss) | |
// fmt.Fprint(out, sss) | |
// fmt.Println("-----------") | |
for _, item := range objects[:0] { | |
obj, ok := item.Object.GetObject_4_() | |
if !ok || ommit(obj) { | |
continue | |
} | |
findAnchor(obj.Content, func(url string) (int, string) { | |
if !isMastodonURL(url) || | |
strings.Contains(url, "/tags/") || | |
strings.Contains(url, "/media/") { | |
if strings.Contains(url, "neetsdkasu") || | |
strings.Contains(url, "usakdsteen") { | |
if strings.Contains(url, "twitter") && strings.Contains(url, "status") { | |
fmt.Println(Toot{obj}.Time().Local(), Toot{obj}.InstanceName(), "=========") | |
fmt.Println(url) | |
} | |
} | |
return -1, "" | |
} | |
if strings.Contains(url, "neetsdkasu") { | |
if !strings.HasSuffix(url, "neetsdkasu") { | |
fmt.Println(Toot{obj}.Time().Local(), Toot{obj}.InstanceName(), "*********") | |
fmt.Println(url) | |
x := fromUrlToId(url) | |
if t, ok := ids[x]; ok { | |
fmt.Println(t.Url) | |
} | |
} | |
return -1, "" | |
} | |
if strings.Contains(url, "@pixiv") { | |
fmt.Println(Toot{obj}.Time().Local(), Toot{obj}.InstanceName(), "#########") | |
fmt.Println(url) | |
// fmt.Println(obj.Content) | |
// fmt.Println(obj.Summary) | |
// fmt.Println(obj.Sensitive) | |
} | |
return -1, "" | |
}) | |
} | |
// fmt.Println("-----------") | |
for _, item := range objects[:0] { | |
obj, ok := item.Object.GetObject_4_() | |
if !ok || ommit(obj) || len(obj.Attachment) == 0 { | |
continue | |
} | |
fmt.Println(Toot{obj}.Time().Local(), Toot{obj}.InstanceName()) | |
for _, a := range obj.Attachment { | |
fmt.Println(a.Url) | |
} | |
} | |
for _, item := range objects[:0] { | |
obj, ok := item.Object.GetObject_4_() | |
if !ok || ommit(obj) || len(obj.Summary) == 0 { | |
continue | |
} | |
fmt.Println(Toot{obj}.Time().Local(), Toot{obj}.InstanceName(), "%%%%%%%%") | |
fmt.Println(obj.Summary) | |
} | |
currentDate = 0 | |
dateList := []int{} | |
postList := [][]*Object_4_{} | |
tempPost := []*Object_4_{} | |
for _, item := range objects { | |
obj, ok := item.Object.GetObject_4_() | |
if !ok || ommit(obj) { | |
continue | |
} | |
t := Toot{obj}.Time() | |
date := t.Year()*10000 + int(t.Month())*100 + t.Day() | |
if date != currentDate { | |
dateList = append(dateList, date) | |
if currentDate != 0 { | |
postList = append(postList, tempPost) | |
tempPost = []*Object_4_{} | |
} | |
currentDate = date | |
} | |
tempPost = append(tempPost, obj) | |
} | |
postList = append(postList, tempPost) | |
fmt.Println(len(dateList)) | |
fmt.Println(len(postList)) | |
categories := []string{"from-md"} | |
const DateFormat = "2006年01月02日" | |
entryName := func(t time.Time) string { | |
return fmt.Sprintf( | |
`<span><time datetime="%s">%s</time>のToots</span>`, | |
t.Format("2006-01-02T15:04:05-07:00"), | |
t.Format(DateFormat), | |
) | |
} | |
for i, posts := range postList { | |
date := Toot{posts[0]}.Time() | |
title := date.Format(DateFormat) + "のToots" | |
body := entryName(date) | |
if i > 0 { | |
prevDayToot := Toot{postList[i-1][0]} | |
prev := fmt.Sprintf( | |
`< <a href="/entry/%s">%s</a> | `, | |
prevDayToot.MTBaseName(), | |
entryName(prevDayToot.Time()), | |
) | |
body = prev + body | |
} | |
if i+1 < len(postList) { | |
nextDayToot := Toot{postList[i+1][0]} | |
body += fmt.Sprintf( | |
` | <a href="/entry/%s">%s</a> >`, | |
nextDayToot.MTBaseName(), | |
entryName(nextDayToot.Time()), | |
) | |
} | |
body = "<p>" + body + "</p>" | |
ext := makeList(ids, posts) + body | |
entry := &BlogEntry{ | |
Author: "Leonardone", | |
Title: title, | |
Year: date.Year(), | |
Month: int(date.Month()), | |
Day: date.Day(), | |
Hour: 23, | |
Minute: 58, | |
Second: 58, | |
Categories: categories, | |
ConvertBreaks: 0, | |
Body: body, | |
ExtendedBody: ext, | |
} | |
fmt.Fprint(out, entry.String()) | |
} | |
forTweet, err1 := os.Create("for_tweet.txt") | |
if err1 != nil { | |
return err1 | |
} | |
defer func() { | |
if tmp := forTweet.Close(); err == nil { | |
err = tmp | |
} | |
}() | |
tweetList := strings.Split(strings.TrimSpace(` | |
https://pawoo.net/@neetsdkasu/1675189 | |
https://mstdn.jp/@neetsdkasu/26310591 | |
https://mstdn.jp/users/neetsdkasu/updates/12709682 | |
https://mstdn.jp/@neetsdkasu/32285368 | |
https://mstdn.jp/users/neetsdkasu/updates/14059113 | |
https://mstdn.jp/@neetsdkasu/36999549 | |
https://mstdn.jp/@neetsdkasu/38015247 | |
https://mstdn.jp/@neetsdkasu/39262835 | |
https://mstdn.jp/@neetsdkasu/44433381 | |
https://pawoo.net/@neetsdkasu/11218455 | |
https://mstdn.jp/@neetsdkasu/98879873964228083 | |
https://mstdn.jp/@neetsdkasu/99026974050479636 | |
https://mstdn.jp/@neetsdkasu/99029607761690441 | |
https://pawoo.net/@neetsdkasu/99457669660041098 | |
https://mstdn.jp/@neetsdkasu/102734731464706809 | |
https://pawoo.net/@neetsdkasu/102745981851453414 | |
`), "\n") | |
empty := Toot{nil} | |
for _, tw := range tweetList { | |
key := strings.TrimSpace(tw) | |
fmt.Fprintf(forTweet, "%q:", key) | |
if key == "https://mstdn.jp/users/neetsdkasu/updates/14059113" { | |
key = "https://mstdn.jp/@neetsdkasu/32486901" | |
} else if key == "https://mstdn.jp/users/neetsdkasu/updates/12709682" { | |
key = "https://mstdn.jp/@neetsdkasu/28326747" | |
} | |
key = fromUrlToId(key) | |
if obj, ok := ids[key]; ok { | |
fmt.Fprintf(forTweet, " %q,", Toot{obj}.Blockquote(ids, empty)) | |
} else { | |
fmt.Println("why?") | |
fmt.Println(tw) | |
fmt.Println(key) | |
} | |
fmt.Fprintln(forTweet) | |
} | |
return | |
} | |
func makeList(allToots ObjectMap, targets []*Object_4_) string { | |
var b strings.Builder | |
fmt.Fprintln(&b, `<ol class="leonardonetoot">`) | |
for _, toot := range targets { | |
t := Toot{toot} | |
rep := t.ReplyCode(allToots, t) | |
text, img := t.BodyCode(allToots) | |
timeStr := t.TimeCode(false) | |
name := t.Name() | |
url := t.UrlCode(t) | |
fmt.Fprintf( | |
&b, | |
`<li id="%s"><div style="font-size: 0.8em; color: grey;">%s %s %s</div>%s%s%s</li>`, | |
makeLinkTargetId(toot.Id), | |
timeStr, | |
name, | |
url, | |
rep, | |
text, | |
img, | |
) | |
fmt.Fprintln(&b) | |
} | |
fmt.Fprintln(&b, "</ol>") | |
return b.String() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "encoding/json" | |
type Object_10_ struct { | |
Items []string `json:"items"` | |
Next string `json:"next"` | |
PartOf string `json:"partOf"` | |
Type string `json:"type"` | |
} | |
type Object_12_ struct { | |
Container string `json:"@container"` | |
Id string `json:"@id"` | |
} | |
type Object_14_ struct { | |
Id string `json:"@id"` | |
Type string `json:"@type"` | |
} | |
type Object_5_ struct { | |
Blurhash string `json:"blurhash"` | |
MediaType string `json:"mediaType"` | |
Name interface{} `json:"name"` | |
Type string `json:"type"` | |
Url string `json:"url"` | |
} | |
type Object_6_ struct { | |
Ar string `json:"ar"` | |
Bg string `json:"bg"` | |
Da string `json:"da"` | |
De string `json:"de"` | |
En string `json:"en"` | |
Es string `json:"es"` | |
Fa string `json:"fa"` | |
Fr string `json:"fr"` | |
Hi string `json:"hi"` | |
HiLatn string `json:"hiLatn"` | |
It string `json:"it"` | |
Ja string `json:"ja"` | |
Kk string `json:"kk"` | |
Nl string `json:"nl"` | |
No string `json:"no"` | |
Pl string `json:"pl"` | |
Pt string `json:"pt"` | |
Ru string `json:"ru"` | |
Sr string `json:"sr"` | |
Zh string `json:"zh"` | |
} | |
type Object_9_ struct { | |
MediaType string `json:"mediaType"` | |
Type string `json:"type"` | |
Url string `json:"url"` | |
} | |
type Object_7_ struct { | |
First *Object_10_ `json:"first"` | |
Id string `json:"id"` | |
Type string `json:"type"` | |
} | |
type Object_11_ struct { | |
Hashtag string `json:"Hashtag"` | |
AtomUri string `json:"atomUri"` | |
Blurhash string `json:"blurhash"` | |
Conversation string `json:"conversation"` | |
FocalPoint *Object_12_ `json:"focalPoint"` | |
InReplyToAtomUri string `json:"inReplyToAtomUri"` | |
Ostatus string `json:"ostatus"` | |
Sensitive string `json:"sensitive"` | |
Toot string `json:"toot"` | |
VotersCount string `json:"votersCount"` | |
} | |
type Mixed_6_ struct { | |
// *Object_14_ | |
// string | |
Value interface{} | |
} | |
func (this *Mixed_6_) UnmarshalJSON(b []byte) (err error) { | |
{ | |
var x *Object_14_ | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
{ | |
var x string | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
return | |
} | |
func (this *Mixed_6_) GetObject_14_() (ret *Object_14_, ok bool) { | |
ret, ok = this.Value.(*Object_14_) | |
return | |
} | |
func (this *Mixed_6_) Object_14_Value() *Object_14_ { | |
return this.Value.(*Object_14_) | |
} | |
func (this *Mixed_6_) GetString() (ret string, ok bool) { | |
ret, ok = this.Value.(string) | |
return | |
} | |
func (this *Mixed_6_) StringValue() string { | |
return this.Value.(string) | |
} | |
type Object_8_ struct { | |
Href string `json:"href"` | |
Icon *Object_9_ `json:"icon"` | |
Id string `json:"id"` | |
Name string `json:"name"` | |
Type string `json:"type"` | |
Updated string `json:"updated"` | |
} | |
type Mixed_4_ struct { | |
// string | |
// *Object_11_ | |
Value interface{} | |
} | |
func (this *Mixed_4_) UnmarshalJSON(b []byte) (err error) { | |
{ | |
var x string | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
{ | |
var x *Object_11_ | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
return | |
} | |
func (this *Mixed_4_) GetString() (ret string, ok bool) { | |
ret, ok = this.Value.(string) | |
return | |
} | |
func (this *Mixed_4_) StringValue() string { | |
return this.Value.(string) | |
} | |
func (this *Mixed_4_) GetObject_11_() (ret *Object_11_, ok bool) { | |
ret, ok = this.Value.(*Object_11_) | |
return | |
} | |
func (this *Mixed_4_) Object_11_Value() *Object_11_ { | |
return this.Value.(*Object_11_) | |
} | |
type Object_13_ struct { | |
Emoji string `json:"Emoji"` | |
Hashtag string `json:"Hashtag"` | |
PropertyValue string `json:"PropertyValue"` | |
AtomUri string `json:"atomUri"` | |
Conversation string `json:"conversation"` | |
Featured *Mixed_6_ `json:"featured"` | |
FocalPoint *Object_12_ `json:"focalPoint"` | |
InReplyToAtomUri string `json:"inReplyToAtomUri"` | |
ManuallyApprovesFollowers string `json:"manuallyApprovesFollowers"` | |
MovedTo *Mixed_6_ `json:"movedTo"` | |
Ostatus string `json:"ostatus"` | |
Schema string `json:"schema"` | |
Sensitive string `json:"sensitive"` | |
Toot string `json:"toot"` | |
Value string `json:"value"` | |
} | |
type Object_4_ struct { | |
AtomUri string `json:"atomUri"` | |
Attachment []*Object_5_ `json:"attachment"` | |
AttributedTo string `json:"attributedTo"` | |
Cc []string `json:"cc"` | |
Content string `json:"content"` | |
ContentMap *Object_6_ `json:"contentMap"` | |
Conversation string `json:"conversation"` | |
Id string `json:"id"` | |
InReplyTo string `json:"inReplyTo"` | |
InReplyToAtomUri string `json:"inReplyToAtomUri"` | |
Published string `json:"published"` | |
Replies *Object_7_ `json:"replies"` | |
Sensitive bool `json:"sensitive"` | |
Summary string `json:"summary"` | |
Tag []*Object_8_ `json:"tag"` | |
To []string `json:"to"` | |
Type string `json:"type"` | |
Url string `json:"url"` | |
} | |
type Mixed_2_ struct { | |
// string | |
// []*Mixed_4_ | |
Value interface{} | |
} | |
func (this *Mixed_2_) UnmarshalJSON(b []byte) (err error) { | |
{ | |
var x string | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
{ | |
var x []*Mixed_4_ | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
return | |
} | |
func (this *Mixed_2_) GetString() (ret string, ok bool) { | |
ret, ok = this.Value.(string) | |
return | |
} | |
func (this *Mixed_2_) StringValue() string { | |
return this.Value.(string) | |
} | |
func (this *Mixed_2_) GetMixed_4_s() (ret []*Mixed_4_, ok bool) { | |
ret, ok = this.Value.([]*Mixed_4_) | |
return | |
} | |
func (this *Mixed_2_) Mixed_4_sValue() []*Mixed_4_ { | |
return this.Value.([]*Mixed_4_) | |
} | |
type Mixed_5_ struct { | |
// string | |
// *Object_13_ | |
Value interface{} | |
} | |
func (this *Mixed_5_) UnmarshalJSON(b []byte) (err error) { | |
{ | |
var x string | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
{ | |
var x *Object_13_ | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
return | |
} | |
func (this *Mixed_5_) GetString() (ret string, ok bool) { | |
ret, ok = this.Value.(string) | |
return | |
} | |
func (this *Mixed_5_) StringValue() string { | |
return this.Value.(string) | |
} | |
func (this *Mixed_5_) GetObject_13_() (ret *Object_13_, ok bool) { | |
ret, ok = this.Value.(*Object_13_) | |
return | |
} | |
func (this *Mixed_5_) Object_13_Value() *Object_13_ { | |
return this.Value.(*Object_13_) | |
} | |
type Mixed_3_ struct { | |
// string | |
// *Object_4_ | |
Value interface{} | |
} | |
func (this *Mixed_3_) UnmarshalJSON(b []byte) (err error) { | |
{ | |
var x string | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
{ | |
var x *Object_4_ | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
return | |
} | |
func (this *Mixed_3_) GetString() (ret string, ok bool) { | |
ret, ok = this.Value.(string) | |
return | |
} | |
func (this *Mixed_3_) StringValue() string { | |
return this.Value.(string) | |
} | |
func (this *Mixed_3_) GetObject_4_() (ret *Object_4_, ok bool) { | |
ret, ok = this.Value.(*Object_4_) | |
return | |
} | |
func (this *Mixed_3_) Object_4_Value() *Object_4_ { | |
return this.Value.(*Object_4_) | |
} | |
type Mixed_1_ struct { | |
// string | |
// []*Mixed_5_ | |
Value interface{} | |
} | |
func (this *Mixed_1_) UnmarshalJSON(b []byte) (err error) { | |
{ | |
var x string | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
{ | |
var x []*Mixed_5_ | |
if err = json.Unmarshal(b, &x); err == nil { | |
this.Value = x | |
return | |
} | |
} | |
return | |
} | |
func (this *Mixed_1_) GetString() (ret string, ok bool) { | |
ret, ok = this.Value.(string) | |
return | |
} | |
func (this *Mixed_1_) StringValue() string { | |
return this.Value.(string) | |
} | |
func (this *Mixed_1_) GetMixed_5_s() (ret []*Mixed_5_, ok bool) { | |
ret, ok = this.Value.([]*Mixed_5_) | |
return | |
} | |
func (this *Mixed_1_) Mixed_5_sValue() []*Mixed_5_ { | |
return this.Value.([]*Mixed_5_) | |
} | |
type Object_3_ struct { | |
Context *Mixed_2_ `json:"@context"` | |
Actor string `json:"actor"` | |
AtomUri string `json:"atomUri"` | |
Cc []string `json:"cc"` | |
Id string `json:"id"` | |
Object *Mixed_3_ `json:"object"` | |
Published string `json:"published"` | |
To []string `json:"to"` | |
Type string `json:"type"` | |
} | |
type Object_2_ struct { | |
Context *Mixed_1_ `json:"@context"` | |
Id string `json:"id"` | |
OrderedItems []*Object_3_ `json:"orderedItems"` | |
TotalItems float64 `json:"totalItems"` | |
Type string `json:"type"` | |
} | |
type Object_1_ *Object_2_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"strings" | |
) | |
func fromUrlToId(url string) string { | |
host := getHostName(url) | |
z := strings.Split(url, "/") | |
return fmt.Sprintf( | |
"https://%s/users/neetsdkasu/statuses/%s", | |
host, | |
z[len(z)-1], | |
) | |
} | |
func getHostName(url string) string { | |
x := strings.TrimPrefix(url, "https://") | |
y := strings.SplitN(x, "/", 2) | |
return y[0] | |
} | |
func makeLinkTargetId(id string) string { | |
x := strings.TrimPrefix(id, "https://") | |
y := strings.SplitN(x, ".", 2) | |
z := strings.Split(id, "/") | |
return y[0] + z[len(z)-1] | |
} | |
func isMastodonURL(url string) bool { | |
return strings.HasPrefix(url, "https://pawoo.net/") || | |
strings.HasPrefix(url, "https://mstdn.jp/") || | |
strings.HasPrefix(url, "https://friends.nico/") | |
} | |
func trimURL(url string) string { | |
url = strings.TrimPrefix(url, "https://") | |
r := []rune(url) | |
var s, t string | |
if len(r) <= 30 { | |
s = url | |
t = "" | |
} else { | |
s = string(r[:30]) | |
t = fmt.Sprintf( | |
`<span class="invisible">%s</span>`, | |
string(r[30:]), | |
) | |
} | |
return fmt.Sprintf( | |
`<span class="invisible">https://</span><span class="ellipsis">%s</span>%s`, | |
s, | |
t, | |
) | |
} | |
func findAnchor(s string, callback func(url string) (int, string)) (ret string) { | |
ret = "" | |
for { | |
sep := strings.SplitN(s, "<a ", 2) | |
if len(sep) < 2 { | |
ret += s | |
return | |
} | |
attr := strings.SplitN(sep[1], `href="`, 2) | |
href := strings.SplitN(attr[1], `"`, 2) | |
rest := strings.SplitN(href[1], ">", 2) | |
body := strings.SplitN(rest[1], "</a>", 2) | |
s = body[1] | |
p, r := callback(href[0]) | |
switch { | |
case p < 0: | |
ret += sep[0] | |
case p == 0: | |
ret += sep[0] + body[0] | |
case p == 1: | |
ret += fmt.Sprintf( | |
`%s<a %shref="%s"%s>%s</a>`, | |
sep[0], attr[0], href[0], rest[0], body[0], | |
) | |
case p > 1: | |
ret += sep[0] + r | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"strings" | |
"text/template" | |
) | |
const MT_TEMP = `AUTHOR: {{.Author}} | |
TITLE: {{.Title}} | |
BASENAME: {{printf "%04d/%02d/%02d/%02d%02d%02d" .Year .Month .Day .Hour .Minute .Second}} | |
STATUS: Publish | |
ALLOW COMMENTS: 1 | |
ALLOW PINGS: 0 | |
CONVERT BREAKS: {{.ConvertBreaks}} | |
PRIMARY CATEGORY: {{index .Categories 0}}{{range .Categories}} | |
CATEGORY: {{.}}{{end}} | |
DATE: {{printf "%02d/%02d/%04d %02d:%02d:%02d" .Month .Day .Year .Hour .Minute .Second}} | |
----- | |
BODY: | |
{{.Body}} | |
----- | |
EXTENDED BODY:{{if .ExtendedBody}} | |
{{.ExtendedBody}}{{end}} | |
----- | |
EXTENDED BODY PRIVATE: | |
----- | |
EXCERPT: | |
----- | |
KEYWORDS: | |
----- | |
-------- | |
` | |
var MTtemplate = template.Must(template.New("MT").Parse(MT_TEMP)) | |
type BlogEntry struct { | |
Author string | |
Title string | |
Year int | |
Month int | |
Day int | |
Hour int | |
Minute int | |
Second int | |
Categories []string | |
ConvertBreaks int | |
Body string | |
ExtendedBody string | |
} | |
func (this *BlogEntry) String() string { | |
var b strings.Builder | |
err := MTtemplate.Execute(&b, this) | |
if err != nil { | |
println(err.Error()) | |
} | |
return b.String() | |
} | |
func GetTestMT(sec int, body, ext string) string { | |
entry := &BlogEntry{ | |
Author: "Leonardone", | |
Title: "テスト投稿", | |
Year: 2020, | |
Month: 6, | |
Day: 1, | |
Hour: 11, | |
Minute: 22, | |
Second: sec, | |
Categories: []string{"testing"}, | |
Body: body, | |
ExtendedBody: ext, | |
} | |
return entry.String() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"strings" | |
"time" | |
) | |
type ObjectMap map[string]*Object_4_ | |
type Toot struct{ obj *Object_4_ } | |
func (this Toot) IsPawoo() bool { | |
return strings.HasPrefix(this.obj.Id, "https://p") | |
} | |
func (this Toot) IsMstdn() bool { | |
return strings.HasPrefix(this.obj.Id, "https://m") | |
} | |
func (this Toot) IsFriends() bool { | |
return strings.HasPrefix(this.obj.Id, "https://f") | |
} | |
func (this Toot) MTBaseName() string { | |
return this.Time().Format("2006/01/02") + "/235858" | |
} | |
func (this Toot) IsSameDate(other Toot) bool { | |
if this.obj == nil || other.obj == nil { | |
return false | |
} | |
t1 := this.Time() | |
t2 := other.Time() | |
return t1.Year() == t2.Year() && | |
t1.Month() == t2.Month() && | |
t1.Day() == t2.Day() | |
} | |
func (this Toot) BodyCode(allToots ObjectMap) (string, string) { | |
refUrls := []string{} | |
pixivToot := "" | |
myTweet := false | |
tweets := []string{} | |
content := findAnchor(this.obj.Content, func(url string) (int, string) { | |
if !isMastodonURL(url) { | |
if strings.HasPrefix(url, "https://twitter.com") && | |
strings.Contains(url, "/status/") { | |
if url == "https://twitter.com/neetsdkasu/status/985238337463107584" { | |
myTweet = true | |
} else { | |
tweets = append(tweets, url) | |
} | |
return -1, "" | |
} | |
return 1, "" | |
} | |
switch { | |
case strings.Contains(url, "/media/"): | |
return -1, "" | |
case strings.Contains(url, "/tags/"): | |
return 0, "" | |
case strings.HasSuffix(url, "@neetsdkasu"): | |
return 2, fmt.Sprintf( | |
`<span class="mastodonuser">@neetsdkasu@%s</span>`, | |
getHostName(url), | |
) | |
case strings.Contains(url, "neetsdkasu"): | |
// たぶん自分のトゥートの引用 | |
println("self,ref?", url) | |
refUrls = append(refUrls, url) | |
return -1, "" | |
default: | |
// 他のアカウントへの言及か他のアカウントの引用トゥート | |
if url == "https://pawoo.net/@itm_nlab" { | |
return 2, `<span class="mastodonuser">@itm_nlab@pawoo.net</span>` | |
} | |
if strings.Contains(url, "@pixiv") { | |
pixivToot = url | |
return -1, "" | |
} | |
println("ref?", url) | |
return 1, "" | |
} | |
}) | |
if len(pixivToot) > 0 { | |
x := strings.SplitN(content, "<p>", 2) | |
y := strings.SplitN(x[1], "</p>", 3) | |
content = fmt.Sprintf( | |
`%s<blockquote><p><a href="%s" target="_blank" rel="nofollow noopener noreferrer external">%s</a></p></blockquote>%s`, | |
x[0], | |
pixivToot, | |
trimURL(pixivToot), | |
y[2], | |
) | |
} | |
if content == "<p></p>" { | |
content = "<p> </p>" | |
} | |
if len(this.obj.Summary) > 0 { | |
summary := fmt.Sprintf( | |
`<div style="border-bottom: 1px dotted;">%s</div>`, | |
this.obj.Summary, | |
) | |
content = summary + content | |
} | |
for _, url := range refUrls { | |
id := fromUrlToId(url) | |
if obj, ok := allToots[id]; ok { | |
content += Toot{obj}.Blockquote(allToots, this) | |
} else if len(allToots) == 0 { | |
content += fmt.Sprintf( | |
`<blockquote><a target="_blank" href="%s" rel="noopener noreferrer nofollow external">%s</a></blockquote>`, | |
url, | |
trimURL(url), | |
) | |
} else { | |
content += fmt.Sprintf( | |
`<blockquote><del><a target="_blank" href="%s" rel="noopener noreferrer nofollow external">%s</a></del></blockquote>`, | |
url, | |
trimURL(url), | |
) | |
} | |
} | |
if myTweet { | |
content += `<blockquote><div style="font-size: 0.8em; color: grey;"><a href="/entry/2018/04/15/235959#tweet985238337463107584" style="text-decoration: none; color: unset;"><time datetime="2018-04-15T04:28:19+09:00">2018年04月15日 04:28:19</time> @neetsdkasu</a> <a style="color: black;" target="_blank" href="https://twitter.com/neetsdkasu/status/985238337463107584" title="https://twitter.com/neetsdkasu/status/985238337463107584" rel="noopener noreferrer external">#</a></div><p>Lv2の問題、誤読が酷くて40分以上も間違った考察してて酷かった、Lv3は残り時間8分のとこで読み始めたがはみるとんへいろ勘違いしててループ検出コードなど出して死んでた…システスの結果2問正解まさかの23位でratingはhighestの無事青色のdiv1に上がれましたとさ(おしまい) </p><a target="_blank" href="https://pbs.twimg.com/media/DaxEQ6KUMAAr2iS.jpg" rel="noopener"><img src="https://pbs.twimg.com/media/DaxEQ6KUMAAr2iS.jpg" width="100" alt="image-0"></a><a target="_blank" href="https://pbs.twimg.com/media/DaxETBVV4AAr7SW.jpg" rel="noopener"><img src="https://pbs.twimg.com/media/DaxETBVV4AAr7SW.jpg" width="100" alt="image-1"></a></blockquote>` | |
} | |
for _, url := range tweets { | |
content += fmt.Sprintf( | |
`<blockquote class="twitter-tweet" data-conversation="none" data-theme="dark"><a href="%s" target="_blank" rel="nofollow noreferrer noopener external">%s</a></blockquote>`, | |
url, | |
trimURL(url), | |
) | |
} | |
img := "" | |
for i, m := range this.obj.Attachment { | |
url := m.Url | |
if this.IsPawoo() { | |
url = "https://img.pawoo.net" + url | |
} else if this.IsMstdn() { | |
url = "https://media.mstdn.jp" + url | |
} | |
img += fmt.Sprintf( | |
`<a target="_blank" href="%s" rel="noopener"><img src="%s" width="100" alt="image-%d"></a>`, | |
url, | |
url, | |
i, | |
) | |
} | |
return content, img | |
} | |
func (this Toot) ReplyCode(allToots ObjectMap, baseDate Toot) string { | |
if len(this.obj.InReplyTo) == 0 { | |
return "" | |
} | |
InReplyTo := this.obj.InReplyTo | |
for { | |
if rep, ok := allToots[InReplyTo]; ok { | |
other := Toot{rep} | |
if this.IsSameDate(other) { | |
return fmt.Sprintf( | |
`<span><a href="#%s">%s</a></span>`, | |
makeLinkTargetId(rep.Id), | |
other.Name(), | |
) | |
} | |
return fmt.Sprintf( | |
`<span><a href="/entry/%s#%s">%s</a></span>`, | |
other.MTBaseName(), | |
makeLinkTargetId(rep.Id), | |
other.Name(), | |
) | |
} else if InReplyTo == "https://pawoo.net/users/neetsdkasu/updates/2022165" { | |
InReplyTo = "https://pawoo.net/users/neetsdkasu/statuses/7059366" | |
} else { | |
return "<span>@ERROR " + this.obj.InReplyTo + "</span>" | |
} | |
} | |
} | |
func (this Toot) UrlCode(dayToot Toot) string { | |
if this.IsFriends() { | |
href := "#" + makeLinkTargetId(this.obj.Id) | |
if !this.IsSameDate(dayToot) { | |
href = "/entry/" + this.MTBaseName() + href | |
} | |
return fmt.Sprintf( | |
`<a style="color: black;" target="_blank" href="%s" rel="noopener" title="%s">#</a>`, | |
href, | |
this.obj.Url, | |
) | |
} else { | |
return fmt.Sprintf( | |
`<a style="color: black;" target="_blank" href="%s" rel="noopener noreferrer nofollow external" title="%s">#</a>`, | |
this.obj.Url, | |
this.obj.Url, | |
) | |
} | |
} | |
var EmptyObjectMap = ObjectMap{} | |
func (this Toot) Blockquote(allToots ObjectMap, dayToot Toot) string { | |
timeStr := this.TimeCode(!this.IsSameDate(dayToot)) | |
name := this.Name() | |
rep := this.ReplyCode(allToots, dayToot) | |
text, img := this.BodyCode(EmptyObjectMap) | |
href := "#" + makeLinkTargetId(this.obj.Id) | |
if !this.IsSameDate(dayToot) { | |
href = "/entry/" + this.MTBaseName() + href | |
} | |
url := this.UrlCode(dayToot) | |
return fmt.Sprintf( | |
`<blockquote><div style="font-size: 0.8em; color: grey;"><a href="%s" style="text-decoration: none; color: unset;">%s %s</a> %s</div>%s%s%s</blockquote>`, | |
href, | |
timeStr, | |
name, | |
url, | |
rep, | |
text, | |
img, | |
) | |
} | |
func (this Toot) Time() time.Time { | |
t, err := time.Parse(time.RFC3339, this.obj.Published) | |
if err != nil { | |
println(err.Error()) | |
} | |
return t.Local() | |
} | |
func (this Toot) TimeCode(full bool) string { | |
t := this.Time() | |
var dt string | |
if full { | |
dt = t.Format("2006年01月02日 15:04:05") | |
} else { | |
dt = t.Format("15:04:05") | |
} | |
return fmt.Sprintf( | |
`<time datetime="%s">%s</time>`, | |
t.Format("2006-01-02T15:04:05-07:00"), | |
dt, | |
) | |
} | |
func (this Toot) InstanceName() string { | |
return getHostName(this.obj.Id) | |
} | |
func (this Toot) Name() string { | |
return "@neetsdkasu@" + this.InstanceName() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment