package main import ( "context" "encoding/json" "flag" "github.com/McKael/madon/v2" "github.com/jaytaylor/html2text" r "gopkg.in/rethinkdb/rethinkdb-go.v6" "within.website/ln" "within.website/ln/opname" ) var ( // 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") ) 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.Account.Acct != *mastodonAccount { 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 NewStatus struct { NewVal *madon.Status `json:"new_val"` OldVal *madon.Status `json:"old_val"` } func (mi *Mi) PushMastodon(ctx context.Context, p Post) error { _, err := mi.mastodonClient.PostStatus(madon.PostStatusParams{ Text: p.Format(), Visibility: "public", }) return err } 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) } if ns.NewVal == nil { continue } st := ns.NewVal if st.Visibility != "public" { continue } if st.Application.Name == "mi_irl" { continue } if st.Sensitive { continue } 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, }) err = r.Table("tweets").Insert(tweet).Exec(mi.session) if err != nil { ln.Error(ctx, err) continue } } }