mi-v1/cmd/mi/mastodon.go

175 lines
3.5 KiB
Go

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
}
}
}