=========================
== Wilderness Downtown ==
=========================

Tweeting images with Golang

Introduction

X (the website formerly known as Twitter) has gone through certain “changes” in recent years. At some point they introduced the v2 API but the functionality isn’t complete.

The v1 API needs to be used for uploading media and getting the resulting media ID. We then need to use the v2 API to reference the uploaded media ID to finalize the post.

Media upload

For the v1 API media upload I utilize this library: github.com/drswork/go-twitter/

This is actually a fork of the now archived github.com/dghubble/go-twitter library who unfortunately did not merge this PR to add media upload functionality. Side note is that I also had an unrelated PR that was never merged :(.

Implementation

Error handling has been removed for brevity.

package main

import (
	"context"
	"encoding/base64"
	"log"
	"os"

	// X API v1
	"github.com/dghubble/oauth1"
	"github.com/drswork/go-twitter/twitter"

	// X API v2
	"github.com/michimani/gotwi"
	"github.com/michimani/gotwi/tweet/managetweet"
	gotwiTypes "github.com/michimani/gotwi/tweet/managetweet/types"
)

const (
	OAuthTokenEnvKeyName       = "GOTWI_ACCESS_TOKEN"
	OAuthTokenSecretEnvKeyName = "GOTWI_ACCESS_TOKEN_SECRET"
)

func main() {
    // A single pixel PNG image
	base64Image := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVR4AWNgAAAAAgABc3UBGAAAAABJRU5ErkJggg=="
	// Decode to []byte
	image, _ := base64.StdEncoding.DecodeString(base64Image)

	postTweetWithMedia("Tweet text", image)
}

func postTweetWithMedia(xMsg string, image []byte) {
	config := oauth1.NewConfig(os.Getenv("GOTWI_API_KEY"), os.Getenv("GOTWI_API_KEY_SECRET"))
	token := oauth1.NewToken(os.Getenv(OAuthTokenEnvKeyName), os.Getenv(OAuthTokenSecretEnvKeyName))
	httpClient := config.Client(oauth1.NoContext, token)

	// v1 API - Upload media
	clientV1 := twitter.NewClient(httpClient)
	media, _, _ := clientV1.Media.Upload(image, "tweet_image")

	// v2 API - Post Tweet with Media
	in := &gotwi.NewClientInput{
		AuthenticationMethod: gotwi.AuthenMethodOAuth1UserContext,
		OAuthToken:           os.Getenv(OAuthTokenEnvKeyName),
		OAuthTokenSecret:     os.Getenv(OAuthTokenSecretEnvKeyName),
	}
	clientV2, _ := gotwi.NewClient(in)

	m := &gotwiTypes.CreateInputMedia{MediaIDs: []string{media.MediaIDString}, TaggedUserID: nil}
	p := &gotwiTypes.CreateInput{
		Text:  gotwi.String(xMsg),
		Media: m,
	}

	output, _ := managetweet.Create(context.Background(), clientV2, p)

	log.Println("Tweet Output:", output.Data.ID)
}