package output import ( "bytes" "context" "errors" "fmt" "os" "github.com/michimani/gotwi" "github.com/michimani/gotwi/media/upload" uploadtypes "github.com/michimani/gotwi/media/upload/types" "github.com/michimani/gotwi/tweet/managetweet" "github.com/michimani/gotwi/tweet/managetweet/types" ) type twitteroutput struct { accessToken string accessSecret string client *gotwi.Client } func TwitterOutput(accessToken string, accessSecret string) *twitteroutput { twitteroutput := &twitteroutput{ accessToken: accessToken, accessSecret: accessSecret, } errOAuth := twitteroutput.newOAuth1Client(accessToken, accessSecret) if errOAuth != nil { fmt.Fprintln(os.Stderr, errOAuth) os.Exit(1) } return twitteroutput } func (to *twitteroutput) newOAuth1Client(accessToken, accessSecret string) error { in := &gotwi.NewClientInput{ AuthenticationMethod: gotwi.AuthenMethodOAuth1UserContext, OAuthToken: accessToken, OAuthTokenSecret: accessSecret, } client, err := gotwi.NewClient(in) to.client = client return err } func (to *twitteroutput) Write(post Post) (Ref, error) { mediaIDs := make([]string, 0, len(post.Images)) for _, img := range post.Images { mediaID, err := to.uploadImage(img) if err != nil { return Ref{}, fmt.Errorf("uploading %s: %w", img.Filename, err) } mediaIDs = append(mediaIDs, mediaID) } p := &types.CreateInput{ Text: gotwi.String(post.Text), } if len(mediaIDs) > 0 { p.Media = &types.CreateInputMedia{ MediaIDs: mediaIDs, } } if post.ReplyTo != nil && post.ReplyTo.ID != "" { p.Reply = &types.CreateInputReply{ InReplyToTweetID: post.ReplyTo.ID, } } out, err := managetweet.Create(context.Background(), to.client, p) if err != nil { return Ref{}, err } if out.Data.ID == nil { return Ref{}, errors.New("created tweet has no ID") } return Ref{ID: *out.Data.ID}, nil } func (to *twitteroutput) Delete(ref Ref) error { if ref.ID == "" { return errors.New("no tweet ID to delete") } _, err := managetweet.Delete(context.Background(), to.client, &types.DeleteInput{ID: ref.ID}) return err } func (to *twitteroutput) uploadImage(img Image) (string, error) { ctx := context.Background() initialized, err := upload.Initialize(ctx, to.client, &uploadtypes.InitializeInput{ MediaCategory: uploadtypes.MediaCategoryTweetImage, MediaType: uploadtypes.MediaType(img.ContentType), TotalBytes: len(img.Data), }) if err != nil { return "", err } mediaID := initialized.Data.MediaID _, err = upload.Append(ctx, to.client, &uploadtypes.AppendInput{ MediaID: mediaID, Media: bytes.NewReader(img.Data), SegmentIndex: 0, }) if err != nil { return "", err } _, err = upload.Finalize(ctx, to.client, &uploadtypes.FinalizeInput{ MediaID: mediaID, }) if err != nil { return "", err } return mediaID, nil } func (to *twitteroutput) GetName() string { return "twitter" }