import.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "encoding/json"
20 "fmt"
21 "html"
22 "io/ioutil"
23 "log"
24 "os"
25 "sort"
26 "strings"
27 "time"
28)
29
30func importMain(username, flavor, source string) {
31 switch flavor {
32 case "twitter":
33 importTwitter(username, source)
34 default:
35 log.Fatal("unknown source flavor")
36 }
37}
38
39func importTwitter(username, source string) {
40 user, err := butwhatabout(username)
41 if err != nil {
42 log.Fatal(err)
43 }
44
45 type Tweet struct {
46 ID_str string
47 Created_at string
48 Full_text string
49 In_reply_to_screen_name string
50 In_reply_to_status_id string
51 Entities struct {
52 Hashtags []struct {
53 Text string
54 }
55 Media []struct {
56 Url string
57 Media_url string
58 }
59 Urls []struct {
60 Url string
61 Expanded_url string
62 }
63 }
64 date time.Time
65 convoy string
66 }
67
68 var tweets []*Tweet
69 fd, err := os.Open(source + "/tweet.js")
70 if err != nil {
71 log.Fatal(err)
72 }
73 // skip past window.YTD.tweet.part0 =
74 fd.Seek(25, 0)
75 dec := json.NewDecoder(fd)
76 err = dec.Decode(&tweets)
77 if err != nil {
78 log.Fatalf("error parsing json: %s", err)
79 }
80 fd.Close()
81 tweetmap := make(map[string]*Tweet)
82 for _, t := range tweets {
83 t.date, _ = time.Parse("Mon Jan 02 15:04:05 -0700 2006", t.Created_at)
84 tweetmap[t.ID_str] = t
85 }
86 sort.Slice(tweets, func(i, j int) bool {
87 return tweets[i].date.Before(tweets[j].date)
88 })
89 for _, t := range tweets {
90 what := "honk"
91 noise := ""
92 if parent := tweetmap[t.In_reply_to_status_id]; parent != nil {
93 t.convoy = parent.convoy
94 what = "tonk"
95 } else {
96 t.convoy = "data:,acoustichonkytonk-" + xfiltrate()
97 if t.In_reply_to_screen_name != "" {
98 noise = fmt.Sprintf("re: https://twitter.com/%s/status/%s\n\n",
99 t.In_reply_to_screen_name, t.In_reply_to_status_id)
100 what = "tonk"
101 }
102 }
103 audience := []string{thewholeworld}
104 xid := fmt.Sprintf("%s/%s/%s", user.URL, honkSep, xfiltrate())
105 honk := Honk{
106 UserID: user.ID,
107 Username: user.Name,
108 What: what,
109 Honker: user.URL,
110 XID: xid,
111 Date: t.date,
112 Format: "markdown",
113 Audience: audience,
114 Convoy: t.convoy,
115 Public: true,
116 Whofore: 2,
117 }
118 noise += t.Full_text
119 // unbelievable
120 noise = html.UnescapeString(noise)
121 for _, r := range t.Entities.Urls {
122 noise = strings.Replace(noise, r.Url, r.Expanded_url, -1)
123 }
124 for _, m := range t.Entities.Media {
125 u := m.Media_url
126 idx := strings.LastIndexByte(u, '/')
127 u = u[idx+1:]
128 fname := fmt.Sprintf("%s/tweet_media/%s-%s", source, t.ID_str, u)
129 data, err := ioutil.ReadFile(fname)
130 if err != nil {
131 log.Printf("error reading media: %s", fname)
132 continue
133 }
134 newurl := fmt.Sprintf("https://%s/d/%s", serverName, u)
135
136 fileid, err := savefile(u, u, u, newurl, "image/jpg", true, data)
137 if err != nil {
138 log.Printf("error saving media: %s", fname)
139 continue
140 }
141 donk := &Donk{
142 FileID: fileid,
143 }
144 honk.Donks = append(honk.Donks, donk)
145 noise = strings.Replace(noise, m.Url, "", -1)
146 }
147 for _, ht := range t.Entities.Hashtags {
148 honk.Onts = append(honk.Onts, "#"+ht.Text)
149 }
150 honk.Noise = noise
151 savehonk(&honk)
152 }
153}