From c2b7e8e5a83028ce64b030ada83d9221df8f940c Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 11 Jan 2020 13:38:50 -0500 Subject: [PATCH] initial commit --- .dockerignore | 1 + .gitignore | 3 + cmd/mi/main.go | 130 ++++++++++++++++++++++++++++++++++++ cmd/mi/mastodon.go | 139 +++++++++++++++++++++++++++++++++++++++ cmd/mi/mi.go | 17 +++++ cmd/mi/patreon.go | 38 +++++++++++ cmd/mi/posse.go | 7 ++ cmd/mi/reddit.go | 141 ++++++++++++++++++++++++++++++++++++++++ cmd/mi/twitter.go | 23 +++++++ go.mod | 26 ++++++++ go.sum | 108 ++++++++++++++++++++++++++++++ tools/paseto-key-gen.go | 21 ++++++ 12 files changed, 654 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 cmd/mi/main.go create mode 100644 cmd/mi/mastodon.go create mode 100644 cmd/mi/mi.go create mode 100644 cmd/mi/patreon.go create mode 100644 cmd/mi/posse.go create mode 100644 cmd/mi/reddit.go create mode 100644 cmd/mi/twitter.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 tools/paseto-key-gen.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e53046a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +bin/ +var/ \ No newline at end of file diff --git a/cmd/mi/main.go b/cmd/mi/main.go new file mode 100644 index 0000000..a41aa1c --- /dev/null +++ b/cmd/mi/main.go @@ -0,0 +1,130 @@ +package main + +import ( + "context" + "flag" + "net/url" + "os" + "os/signal" + "time" + + "github.com/facebookarchive/flagenv" + _ "github.com/joho/godotenv/autoload" + r "gopkg.in/rethinkdb/rethinkdb-go.v6" + "within.website/ln" +) + +var ( + // Twitter + twitterAPIKey = flag.String("twitter-api-key", "", "Twitter API key") + twitterAPISecret = flag.String("twitter-api-secret", "", "Twitter API secret") + twitterConsumerToken = flag.String("twitter-consumer-token", "", "Twitter consumer Token") + twitterConsumerSecret = flag.String("twitter-consumer-secret", "", "Twitter consumer Secret") + + // Mastodon + mastodonInstance = flag.String("mastodon-instance", "", "Mastodon instance to connect to") + mastodonAppID = flag.String("mastodon-app-id", "", "Mastodon app ID") + mastodonAppSecret = flag.String("mastodon-app-secret", "", "Mastodon app secret") + mastodonToken = flag.String("mastodon-token", "", "Mastodon API token") + mastodonAccount = flag.String("mastodon-account", "", "Mastodon account") + + /* + // Gitea + giteaInstance = flag.String("gitea-instance", "", "Gitea instance to use") + giteaAPIToken = flag.String("gitea-api-token", "", "Gitea API token") + + // Github + githubToken = flag.String("github-token", "", "Github API token") + */ + + // Reddit + redditAppID = flag.String("reddit-app-id", "", "Reddit app ID") + redditAppSecret = flag.String("reddit-app-secret", "", "Reddit app Secret") + redditUsername = flag.String("reddit-username", "", "Reddit username") + redditPassword = flag.String("reddit-password", "", "Reddit password") + + // RethinkDB + rethinkDBURL = flag.String("rethinkdb-url", "rethinkdb2://admin@127.0.0.1:28015/mi", "RethinkDB URL") + + // Patreon + patreonClientID = flag.String("patreon-client-id", "", "Patreon client ID") + patreonClientSecret = flag.String("patreon-client-secret", "", "Patreon client secret") + patreonAccessToken = flag.String("patreon-access-token", "", "Patreon access token") + patreonRefreshToken = flag.String("patreon-refresh-token", "", "Patreon refresh token") + + // Blog + blogURL = flag.String("blog-url", "https://christine.website/blog.json", "JSONFeed to monitor for new posts") + + // Port + port = flag.String("port", "5000", "HTTP port") +) + +func main() { + flagenv.Parse() + flag.Parse() + + ctx := context.Background() + ctx = ln.WithF(ctx, ln.F{ + "mastodon_instance": *mastodonInstance, + }) + + r.SetTags("rethinkdb", "json") + + u, err := url.Parse(*rethinkDBURL) + if err != nil { + ln.FatalErr(ctx, err) + } + + pw, _ := u.User.Password() + db := u.Path[1:] + + session, err := r.Connect(r.ConnectOpts{ + Address: u.Host, + Database: db, + Username: u.User.Username(), + Password: pw, + }) + if err != nil { + ln.FatalErr(ctx, err) + } + + mastodonClient, err := makeMastodon() + if err != nil { + ln.FatalErr(ctx, err) + } + + redditBot, err := makeReddit() + if err != nil { + ln.FatalErr(ctx, err) + } + + twitterClient, err := makeTwitter() + if err != nil { + ln.FatalErr(ctx, err) + } + + mi := &Mi{ + session: session, + mastodonClient: mastodonClient, + twitterClient: twitterClient, + redditBot: redditBot, + } + + ctx, cancel := context.WithCancel(ctx) + go func(cf func()) { + var signalChan chan os.Signal + signalChan = make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt) + + <-signalChan + cf() + }(cancel) + + go mi.StreamMastodon(ctx) + go mi.StreamReddit(ctx) + go mi.StreamMastodonToTwitter(ctx) + + <-ctx.Done() + ln.Log(ctx, ln.Info("cleaning up")) + time.Sleep(500 * time.Millisecond) +} diff --git a/cmd/mi/mastodon.go b/cmd/mi/mastodon.go new file mode 100644 index 0000000..c60f409 --- /dev/null +++ b/cmd/mi/mastodon.go @@ -0,0 +1,139 @@ +package main + +import ( + "context" + "encoding/json" + + "github.com/McKael/madon" + "github.com/jaytaylor/html2text" + r "gopkg.in/rethinkdb/rethinkdb-go.v6" + "within.website/ln" + "within.website/ln/opname" +) + +func makeMastodon() (*madon.Client, error) { + c, err := madon.RestoreApp("mi", *mastodonInstance, *mastodonAppID, *mastodonAppSecret, &madon.UserToken{AccessToken: *mastodonToken}) + if err != nil { + return nil, err + } + + return c, nil +} + +func (mi *Mi) StreamMastodon(ctx context.Context) error { + evChan := make(chan madon.StreamEvent, 10) + stop := make(chan bool) + done := make(chan bool) + ctx = opname.With(context.Background(), "user-stream") + + err := mi.mastodonClient.StreamListener("user", "", evChan, stop, done) + if err != nil { + ln.FatalErr(ctx, err) + } + + ln.Log(ctx, ln.Info("streaming user toots to rethinkdb")) + + for { + select { + case <-done: + ln.Fatal(ctx, ln.Action("got done?")) + case <-ctx.Done(): + stop <- true + case ev := <-evChan: + s, ok := ev.Data.(madon.Status) + if !ok { + continue + } + + if s.Reblog != nil { + continue + } + + err := r.Table("mastodon").Insert(s).Exec(mi.session) + if err != nil { + ln.Error(ctx, err) + stop <- true + } + + ln.Log(ctx, ln.Info("got toot"), ln.F{ + "toot_url": s.URL, + "toot_creator": s.Account.Username, + }) + } + } +} + +type MastodonStatus struct { + ID float64 `json:"id"` + InReplyToID *float64 `json:"in_reply_to_id"` + Account struct { + Acct string `json:"acct"` + } `json:"account"` + URL string `json:"url"` + Content string `json:"content"` +} + +type NewStatus struct { + NewVal *MastodonStatus `json:"new_val"` + OldVal *MastodonStatus `json:"old_val"` +} + +func (mi *Mi) StreamMastodonToTwitter(ctx context.Context) { + res, err := r.Table("mastodon").Changes().Run(mi.session) + if err != nil { + ln.FatalErr(ctx, err) + } + defer res.Close() + + ln.Log(ctx, ln.Info("streaming mastodon to twitter")) + + for { + data, ok := res.NextResponse() + if !ok { + ln.FatalErr(ctx, res.Err()) + } + + select { + case <-ctx.Done(): + break + default: + } + + var ns NewStatus + err := json.Unmarshal(data, &ns) + if err != nil { + ln.FatalErr(ctx, err) + } + st := ns.NewVal + + if st.Account.Acct != *mastodonAccount { + continue + } + + if st.InReplyToID != nil { + continue + } + + text, err := html2text.FromString(st.Content, html2text.Options{OmitLinks: true}) + if len(text) > 200 { + text = text[:200] + "... read more here: " + st.URL + } + if err != nil { + ln.Error(ctx, err, ln.F{"url": st.URL}) + continue + } + + tweet, _, err := mi.twitterClient.Statuses.Update(text, nil) + if err != nil { + ln.Error(ctx, err, ln.F{ + "text": text, + "url": st.URL, + }) + continue + } + + ln.Log(ctx, ln.Info("sent tweet from mastodon"), ln.F{ + "tweet_id": tweet.ID, + }) + } +} diff --git a/cmd/mi/mi.go b/cmd/mi/mi.go new file mode 100644 index 0000000..195c406 --- /dev/null +++ b/cmd/mi/mi.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/McKael/madon" + "github.com/dghubble/go-twitter/twitter" + "github.com/turnage/graw/reddit" + "gopkg.in/mxpv/patreon-go.v1" + r "gopkg.in/rethinkdb/rethinkdb-go.v6" +) + +type Mi struct { + session *r.Session + mastodonClient *madon.Client + twitterClient *twitter.Client + redditBot reddit.Bot + patreonClient *patreon.Client +} diff --git a/cmd/mi/patreon.go b/cmd/mi/patreon.go new file mode 100644 index 0000000..6145dc0 --- /dev/null +++ b/cmd/mi/patreon.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + "time" + + "golang.org/x/oauth2" + "gopkg.in/mxpv/patreon-go.v1" +) + +func makePatreon() (*patreon.Client, error) { + config := oauth2.Config{ + ClientID: *patreonClientID, + ClientSecret: *patreonClientSecret, + Endpoint: oauth2.Endpoint{ + AuthURL: patreon.AuthorizationURL, + TokenURL: patreon.AccessTokenURL, + }, + Scopes: []string{"users", "pledges-to-me", "my-campaign"}, + } + + token := oauth2.Token{ + AccessToken: *patreonAccessToken, + RefreshToken: *patreonRefreshToken, + // Must be non-nil, otherwise token will not be expired + Expiry: time.Now().Add(-24 * time.Hour), + } + + tc := config.Client(context.Background(), &token) + + client := patreon.NewClient(tc) + _, err := client.FetchUser() + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/cmd/mi/posse.go b/cmd/mi/posse.go new file mode 100644 index 0000000..78ff78e --- /dev/null +++ b/cmd/mi/posse.go @@ -0,0 +1,7 @@ +package main + +type Post struct { + Title string + URL string + Tags []string +} diff --git a/cmd/mi/reddit.go b/cmd/mi/reddit.go new file mode 100644 index 0000000..1410936 --- /dev/null +++ b/cmd/mi/reddit.go @@ -0,0 +1,141 @@ +package main + +import ( + "context" + "runtime" + "strings" + + "github.com/turnage/graw/reddit" + "github.com/turnage/graw/streams" + r "gopkg.in/rethinkdb/rethinkdb-go.v6" + "within.website/ln" +) + +func makeReddit() (reddit.Bot, error) { + cfg := reddit.BotConfig{ + Agent: "mi:life_logging_bot:0.0.1 by /u/shadowh511", + App: reddit.App{ + ID: *redditAppID, + Secret: *redditAppSecret, + Username: *redditUsername, + Password: *redditPassword, + }, + } + + bot, err := reddit.NewBot(cfg) + if err != nil { + return nil, err + } + + return bot, nil +} + +func (mi *Mi) StreamReddit(ctx context.Context) { + kill := make(chan bool) + errs := make(chan error, 5) + + posts, comments, err := streams.User(mi.redditBot, kill, errs, *redditUsername) + if err != nil { + ln.FatalErr(ctx, err) + } + + postReplies, err := streams.PostReplies(mi.redditBot, kill, errs) + if err != nil { + ln.FatalErr(ctx, err) + } + + commentReplies, err := streams.CommentReplies(mi.redditBot, kill, errs) + if err != nil { + ln.FatalErr(ctx, err) + } + + ln.Log(ctx, ln.Info("streaming reddit posts")) + + for { + select { + case <-ctx.Done(): + kill <- true + kill <- true + kill <- true + runtime.Goexit() + case <-errs: + ln.Error(ctx, err) + case p := <-posts: + ln.Log(ctx, ln.Info("got reddit post"), ln.F{ + "from": p.Author, + "link": p.Permalink, + }) + + data := map[string]interface{}{ + "id": p.Name, + "permalink": p.Permalink, + "created_at": p.CreatedUTC, + "author": p.Author, + "body": p.SelfText, + "url": p.URL, + } + + err := r.Table("reddit_posts").Insert(data).Exec(mi.session) + if err != nil { + ln.Error(ctx, err) + } + case c := <-comments: + ln.Log(ctx, ln.Info("got reddit comment"), ln.F{ + "id": c.ID, + "author": c.Author, + "thread": strings.Join(strings.Split(c.Permalink, "/")[:6], "/") + "/", + }) + + data := map[string]interface{}{ + "id": c.ID, + "author": c.Author, + "parent_id": c.ParentID, + "body": c.Body, + "thread": strings.Join(strings.Split(c.Permalink, "/")[:6], "/") + "/", + } + + err := r.Table("reddit_comments").Insert(data).Exec(mi.session) + if err != nil { + ln.Error(ctx, err) + } + case pr := <-postReplies: + ln.Log(ctx, ln.Info("got reddit post reply"), ln.F{ + "id": pr.ID, + "author": pr.Author, + "thread": strings.Join(strings.Split(pr.Context, "/")[:6], "/") + "/", + }) + + data := map[string]interface{}{ + "id": pr.ID, + "author": pr.Author, + "parent_id": pr.ParentID, + "body": pr.Body, + "thread": strings.Join(strings.Split(pr.Context, "/")[:6], "/") + "/", + } + + err := r.Table("reddit_comments").Insert(data).Exec(mi.session) + if err != nil { + ln.Error(ctx, err) + } + case cr := <-commentReplies: + ln.Log(ctx, ln.Info("got reddit comment reply"), ln.F{ + "id": cr.ID, + "author": cr.Author, + "thread": strings.Join(strings.Split(cr.Context, "/")[:6], "/") + "/", + }) + + data := map[string]interface{}{ + "id": cr.ID, + "author": cr.Author, + "parent_id": cr.ParentID, + "body": cr.Body, + "thread": strings.Join(strings.Split(cr.Context, "/")[:6], "/") + "/", + } + + err := r.Table("reddit_comments").Insert(data).Exec(mi.session) + if err != nil { + ln.Error(ctx, err) + } + } + } +} diff --git a/cmd/mi/twitter.go b/cmd/mi/twitter.go new file mode 100644 index 0000000..0ad518d --- /dev/null +++ b/cmd/mi/twitter.go @@ -0,0 +1,23 @@ +package main + +import ( + "github.com/dghubble/go-twitter/twitter" + "github.com/dghubble/oauth1" +) + +func makeTwitter() (*twitter.Client, error) { + config := oauth1.NewConfig(*twitterConsumerToken, *twitterConsumerSecret) + token := oauth1.NewToken(*twitterAPIKey, *twitterAPISecret) + // http.Client will automatically authorize Requests + httpClient := config.Client(oauth1.NoContext, token) + + // twitter client + client := twitter.NewClient(httpClient) + + _, _, err := client.Accounts.VerifyCredentials(nil) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2093b5c --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module within.website/mi + +go 1.13 + +require ( + github.com/McKael/madon v2.3.0+incompatible + github.com/dghubble/go-twitter v0.0.0-20190719072343-39e5462e111f + github.com/dghubble/oauth1 v0.6.0 + github.com/facebookarchive/flagenv v0.0.0-20160425205200-fcd59fca7456 + github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect + github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 + github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect + github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect + github.com/gorilla/websocket v1.4.1 // indirect + github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43 + github.com/joho/godotenv v1.3.0 + github.com/kr/pretty v0.1.0 + github.com/olekukonko/tablewriter v0.0.4 // indirect + github.com/sendgrid/rest v2.4.1+incompatible // indirect + github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect + github.com/turnage/graw v0.0.0-20191224200831-a592320d5bc9 + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + gopkg.in/mxpv/patreon-go.v1 v1.0.0-20171031001022-1d2f253ac700 + gopkg.in/rethinkdb/rethinkdb-go.v6 v6.0.0 + within.website/ln v0.7.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..26a3ec2 --- /dev/null +++ b/go.sum @@ -0,0 +1,108 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/McKael/madon v2.3.0+incompatible h1:xMUA+Fy4saDV+8tN3MMnwJUoYWC//5Fy8LeOqJsRNIM= +github.com/McKael/madon v2.3.0+incompatible/go.mod h1:+issnvJjN1rpjAHZwXRB/x30uHh/NoQR7QaojJK/lSI= +github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY= +github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dghubble/go-twitter v0.0.0-20190719072343-39e5462e111f h1:M2wB039zeS1/LZtN/3A7tWyfctiOBL4ty5PURBmDdWU= +github.com/dghubble/go-twitter v0.0.0-20190719072343-39e5462e111f/go.mod h1:xfg4uS5LEzOj8PgZV7SQYRHbG7jPUnelEiaAVJxmhJE= +github.com/dghubble/oauth1 v0.6.0 h1:m1yC01Ohc/eF38jwZ8JUjL1a+XHHXtGQgK+MxQbmSx0= +github.com/dghubble/oauth1 v0.6.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk= +github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU= +github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY= +github.com/facebookarchive/flagenv v0.0.0-20160425205200-fcd59fca7456 h1:1NzATV1A9LeTWAuAJGd7vE/wYSO08EJQTrwKcBBR6Cw= +github.com/facebookarchive/flagenv v0.0.0-20160425205200-fcd59fca7456/go.mod h1:Lbo+w7sC1xznzryrqUDoBBT2nLKV9JnDOzdiVMHYX/8= +github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= +github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456 h1:CkmB2l68uhvRlwOTPrwnuitSxi/S3Cg4L5QYOcL9MBc= +github.com/facebookgo/flagenv v0.0.0-20160425205200-fcd59fca7456/go.mod h1:zFhibDvPDWmtk4dAQ05sRobtyoffEHygEt3wSNuAzz8= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= +github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43 h1:jTkyeF7NZ5oIr0ESmcrpiDgAfoidCBF4F5kJhjtaRwE= +github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sendgrid/rest v2.4.1+incompatible h1:HDib/5xzQREPq34lN3YMhQtMkdXxS/qLp5G3k9a5++4= +github.com/sendgrid/rest v2.4.1+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= +github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= +github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/turnage/graw v0.0.0-20191224200831-a592320d5bc9 h1:hGzA+rD6+ZhT23x6lG6Pk6tG3gI95gvNu0X5MzwoYkQ= +github.com/turnage/graw v0.0.0-20191224200831-a592320d5bc9/go.mod h1:aAkq4I/q1izZSSwHvzhDn9NA+eGxgTSuibwP3MZRlQY= +github.com/turnage/redditproto v0.0.0-20151223012412-afedf1b6eddb h1:qR56NGRvs2hTUbkn6QF8bEJzxPIoMw3Np3UigBeJO5A= +github.com/turnage/redditproto v0.0.0-20151223012412-afedf1b6eddb/go.mod h1:GyqJdEoZSNoxKDb7Z2Lu/bX63jtFukwpaTP9ZIS5Ei0= +golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac h1:7d7lG9fHOLdL6jZPtnV4LpI41SbohIJ1Atq7U991dMg= +golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b h1:cmOZLU2i7CLArKNViO+ZCQ47wqYFyKEIpbGWp+b6Uoc= +golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fatih/pool.v2 v2.0.0 h1:xIFeWtxifuQJGk/IEPKsTduEKcKvPmhoiVDGpC40nKg= +gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY= +gopkg.in/mxpv/patreon-go.v1 v1.0.0-20171031001022-1d2f253ac700 h1:ymnLBRNALxuok6al+nlPJxfSa3yc2SZc5N21svHQtys= +gopkg.in/mxpv/patreon-go.v1 v1.0.0-20171031001022-1d2f253ac700/go.mod h1:IZaw6NfbSsGszLfPbo9LLlxLIx17eMHWe4cxpM8wUMk= +gopkg.in/rethinkdb/rethinkdb-go.v6 v6.0.0 h1:x+tVYhL4QUktgB8Dvspq0ZmnPYMO/5tYEvhX5KBm47M= +gopkg.in/rethinkdb/rethinkdb-go.v6 v6.0.0/go.mod h1:Z9NgaOLdqIZS9v3EFLP/S0VdloWSW+2zEN6dllUSCVQ= +within.website/ln v0.7.0 h1:cZUc53cZF/+hWuEAv1VbqlYJ5czuPFHKfH0hLKmlIUA= +within.website/ln v0.7.0/go.mod h1:ifURKqsCJekcsdUE+hyCdcuhQqQ+9v9DfA++ZqYxZFE= diff --git a/tools/paseto-key-gen.go b/tools/paseto-key-gen.go new file mode 100644 index 0000000..738ba47 --- /dev/null +++ b/tools/paseto-key-gen.go @@ -0,0 +1,21 @@ +//+build ignore + +package main + +import ( + "crypto/ed25519" + "encoding/hex" + "fmt" +) + +func main() { + publicKey, privateKey, err := ed25519.GenerateKey(nil) + if err != nil { + panic(err) + } + fmt.Printf( + "PASETO_PUBLIC_KEY=%s\nPASETO_PRIVATE_KEY=%s\n", + hex.EncodeToString(publicKey), + hex.EncodeToString([]byte(privateKey)), + ) +}