ropensci-archive/rtweet

tweet_post with media

Closed this issue · 12 comments

llrs commented

Provide an example of how to tweet_post with an image.
Currently requires combining the API v1.1 for uploading media.

See how easy it is or make it easier for users.

This would be fantastic. My niche (and somewhat silly) application busted with the 1.1 deprecation/pseudo-deprecation. It's giving me an excuse to do some further noodling with rtweet, but, at this point, I think I've just validated that the fix of what broke for me is a bit beyond my skills. And this issue reads like it would be the assist I'm looking for.

llrs commented

Currently I'm working on another package, I hope to finish the updates soon and then I'm planning to set to work on this. But I don't have any estimate on when I'll be doing this.
If I am lucky a company will sponsor the development of this feature and then I might do it sooner.

Initially I thought a user could do it, but after thinking it more it won't be able to do so without using internal functions of rtweet. I don't advise to propose/use a solution if not in a pull request.

Hello,

I've been needing this functionality for my personal project, @BBLMaths

Here's the code I've got working. It's not elegant as it mixes httr, httr2 and rwteet, but it's functional, so I thought I'd share

You need to authenticate differently for each end point, upload the media, then reference the media id in the POST request

# Posting a tweet with an image via API V1.1 and API V2

# Libraries
library(rtweet)
library(httr2)
library(httr)
library(base64enc)


### Need to do two authentications
# Authentication for API V2

# See rtweet vignette for this
client_as("YOUR_CLIENT")

# Initiate confirmation via browser
oauth2 <- rtweet_oauth2(client = client)
# Lasts for 2 hours

# Authentication for API V1.1 (needed to load image)

# Twitter API credentials
app <- "YOUR_APP"
consumer_key <- "YOUR_CONSUMER_KEY"
consumer_secret <- "YOUR_CONSUMER_SECRET"
access_token <- "YOUR_ACCESS_TOKEN"
access_token_secret <- "YOUR_ACCESS_TOKEN_SECRET"

your_token <- create_token(
  app = app,
  consumer_key = api_key,
  consumer_secret = api_secret_key,
  access_token = access_token,
  access_secret = access_token_secret)

# Path to the image file
image_path <- "path_to_your_image.png"

# Read and encode image
image_data <- readBin(image_path, "raw", file.info(image_path)$size)
image_base64 <- base64encode(image_data)

# Twitter API endpoint for media upload
media_upload_url <- "https://upload.twitter.com/1.1/media/upload.json"


# Create the API request
response <- POST(
  url = media_upload_url,
  config(token = your_token),
  body = list(media = upload_file(image_path)),
  encode = "multipart"
)

# Parse the response
parsed_response <- content(response, "parsed")

# Extract media ID from the response
media_id <- parsed_response$media_id_string


### Now post a tweet

# set end point
req <- request("https://api.twitter.com/2/tweets")

# set up and run the tweet
req %>%
  req_body_json(list(text = "testing with httr2"
                     , media = list(media_ids = list(as.character(media_id))
                     )
  )
  ) %>%
  req_auth_bearer_token(oauth2$access_token) %>%
  req_perform()
llrs commented

Thanks @AlistairGilfillan, this is more or less how I would do it (using the internal functions of rtweet). Also uploading the image to the media_upload_url could be done with httr2. Maybe the setting the two authentications can be somewhat minimized as there is a mechanism to automatically request one bearer token. (I edited your post to make it easier to read the code). Many thanks for contributing.

Note: if you want the oauth2 authentication to last longer (and renew automatically), you'll need httr2 development version and rtweet from the dev branch too.

Thanks @llrs for amending the code (and maintaining this package).
I'll amend the media upload to httr2 and edit the example above when I've figured it out.

On the oath2 stuff - when I authenticate I have a manual step of clicking I need to do when I run:
oauth2 <- rtweet_oauth2(client = client)
Is there a way to avoid this?
My previous workflow using the create_token() approach meant I could run a scheduled script to check if I needed a tweet, then post the tweet. Currently I'd need to click for each script I run. If I can avoid the click I should be able to run as I did previously, otherwise I think the only option is to run a single script with renewing authentication which then checks if certain triggers are met, then posting tweets if necessary

llrs commented

Ok, great! I prefer to keep the API v2 as httr2, but might decide to use httr2 as this would allow me to drop the API v1 and the httr dependency at the same time (I take that the current situation won't change all of a sudden, [except for closing]).

As I mentioned in the previous comment with the development version of httr2 and the devel branch of rtweet it is possible to avoid further authentication via the browser (only the first one is needed):

remotes::install_github("r-lib/httr2")
remotes::install_github("ropensci/rtweet@devel")

Note however that other things might break there, but at least this issue is not present 😄 .

Alistair - this is magnificent. It ALMOST works for me. I can tell it really WANTS to work.

My only trouble is in the very last step, which throws this:
Error in req_perform():
! HTTP 400 Bad Request.

Now, I'm not a dev guy so I haven't a clue what's causing this. All of my environment variables look correctly set. I have no experience with http/JSON commands so I'm hoping the reason for this is super obvious and you can point me.

But...in any event...thank you so much for getting me soooooo close to this...

Hi @MikeAshtonEILLC
If you replace req_perform() with req_dry_run() you’ll see the request without it being sent. You could then check this against the Twitter API documentation for the /2/tweets endpoint

I think the error message means either a typo within the req <- …. Line or an issue with the body.
I would try to isolate the issue in the body by commenting out the media line, if it works without that then you’re one step closer to diagnosing the problem
I think the media_id only works the first time a tweet references it, and expires if not used within a given timeframe. So, just re-running the code from authentication might work

Also check you’ve got the two instances of the list() function on the media line. Took me a while to figure out this was needed, easy to overlook. (Functionally it appears to add some square brackets that I saw were needed in the documentation

Eureka!

So, the problem was that I have two browser setups. Firefox with one persona, Chrome with the persona I am posting to. Problem is, the code kept authorizing on the Firefox persona, and then couldn't post. And I couldn't get it to point to Chrome because Firefox is my default browser (I assume that's the problem).

Easy fix: log into Firefox with the persona I want to post with. Worked like a charm! Thanks a million @AlistairGilfillan, and of course @llrs for making all of this possible in the first place!

Nice work figuring it out @MikeAshtonEILLC
I’m let me know if you get code for the automatic renewal. Fighting that and interaction with scheduled scripts is next on my list

llrs commented

Yes, the authorization request is opened in the default browser.You can change the browser R opens via options(browser = "path/to/browser"). See this question & answer.

llrs commented

This package has been archived, you can request for it to be unarchived if you opt to resume maintenance, for that please contact rOpenSci.