Twitter API v1.1 update_profile_banner从r内部

发布于 2025-02-08 08:25:56 字数 9535 浏览 4 评论 0原文

我想使用Twitter api v1.1更新我的Twitter横幅profle 。软件包(我知道的软件包,没有这种功能),也可以是调用system2(“ curl”,args = args_strng)或任何其他方法 - 只要它是在下面进行。

我将详细解释到目前为止尝试的方法。


方法1:使用{rtweet}(v0.7.0)

{rtweet}是用于与Twitter API v1.1交互的最流行的R软件包。它带有许多功能,但是更新横幅配置文件 尚未支持。

这个想法是在{rtweet}上创建一个功能构建,该功能构建允许将文件路径链接到适当大小的图像(1500x500 px),以将其作为新的Twitter横幅上传。

update_profile_banner <- function (banner_file, token = NULL) {

  token <- rtweet:::check_token(token)

  banner_uri <- base64enc::base64encode(banner_file)

  query <- "account/update_profile_banner"

  # build params
  params <- list(banner = banner_uri)

  # make URL
  url <- rtweet:::make_url(query = query, param = params)

  # send
  r <- rtweet:::TWIT(get = FALSE, url, token)

  if (!r$status_code %in% c(200,201,202)) {
    return(httr::content(r))
  }

  message("your profile banner image has been updated!")
}

# Now all we need is a new banner image (see `test_png` below) and a valid Twitter token:

library(rtweet) # v.0.7.0
library(httr) # v.1.4.2

# Create a token containing Twitter keys
mytoken <- rtweet::create_token(
  app = "the name of the Twitter app", 
  consumer_key = Sys.getenv("MY_CONSUMER_API_KEY"),
  consumer_secret = Sys.getenv("MY_CONSUMER_API_SECRET"),
  access_token = Sys.getenv("MY_ACCESS_TOKEN"),
  access_secret = Sys.getenv("MY_ACCESS_TOKEN_SECRET"),
  set_renv = FALSE
)

test_png <- "https://raw.githubusercontent.com/TimTeaFan/dynamicTwitterHeader/main/data/test.png"

update_profile_banner(test_png, mytoken)
#> Error in curl::curl_fetch_memory(url, handle = handle): HTTP/2 stream 0 was not closed cleanly: INTERNAL_ERROR (err 2)

错误消息并不是很有帮助,但是查找并更改HTTP版本2至1.1会给我们提供更有意义的消息,告诉我们标题太长了。 我怀疑这是由于以下事实:在引擎盖下{httr}调用以下curl命令:

POST https://api.twitter.com/1.1/account/update_profile_banner.json?banner=super_long_image_string_goes_here+oauth_credentials_come_last

它只是一个简单的post命令,而不是在标头和身体之间区分,(我的猜测)由于图像字符串而导致标头太长。

要检查此假设是正确的(或至少没有错),并排除该方法本身是错误的可能性,我创建了与获取横幅映像

get_profile_banner <- function (screen_name, token) {

  token <- rtweet:::check_token(token)

  query <- "users/profile_banner"

  # build params
  params <- list(screen_name = screen_name)

  # make URL
  url <- rtweet:::make_url(query = query, param = params)
  r <- rtweet:::TWIT(get = TRUE, url, token)

    if (!r$status_code %in% c(200,201,202)) {
    return(httr::content(r))
  }

  rtweet:::from_js(r)
}

get_profile_banner("timteafan", mytoken)

#> $sizes
#> $sizes$ipad
#> $sizes$ipad$h
#> [1] 313
#> 
#> $sizes$ipad$w
#> [1] 626
#> 
#> $sizes$ipad$url
#> [1] "https://pbs.twimg.com/profile_banners/188839854/1653511325/ipad"
#> 
#> ...
#> [truncated]

因为get_profile_banner()工作正常,我怀疑问题确实是由于我们正在用简单的调用curl post post发送到Twitter API的长base64编码图像字符串。


方法2:使用system2(“ curl”)

因此可能的解决方法可能是通过curl直接使用curl使用system2()来构建一个更复杂的呼叫,将包含授权的标题与包含图像字符串的主体分开。

这里唯一的问题是我们必须创建自己的签名。我尝试了一下,但失败了,所以我决定使用{httr}来创建签名和所有OAuth凭据,然后将其用作标头。

让我们首先从get_banner_profile()开始,以查看此方法是否有效。

为此,我们需要一些功能和辅助功能:

主要功能

get_banner <- function(screen_name, token) {

  query <- "users/profile_banner"

  url <- rtweet:::make_url(query = query,
                           param = list(screen_name = screen_name))

  # lets create the oauth credentials
  oauth <- oauth_GET(url, token)
  oauth <- oauth[[which(names(oauth) == "Authorization")]]

  # this is the initial curl command
  req <- "--get 'https://api.twitter.com/1.1/users/profile_banner.json?'"

  # this is the data
  data <- paste0("--data 'screen_name=", screen_name, "'")

  # and here goes the header
  header <- paste0("--header 'Authorization:", oauth, "'")

  # lets collapse everything into one string
  args <- paste(req, data, header, collapse = " ")
  system2("curl", args = args)
}

这是一些助手功能的

oauth_GET <- function (url = NULL, config = list(), ..., handle = NULL) {
  hu <- httr:::handle_url(handle, url, ...)
  req <- httr:::request_build("GET", hu$url, as.token.request(config), ...) 
  oauth_req(req, hu$handle$handle)
}

oauth_req <- function (req, handle) {
  stopifnot(httr:::is.request(req), inherits(handle, "curl_handle"))
  req <- httr:::request_prepare(req)
  req$headers
}

# httr methods
as.token.request <- function(x) auth_req(auth_token = x)

auth_req <- function (auth_token) {
  structure(list(auth_token = auth_token), class = "request")
}

library(httr)
get_banner("timteafan", mytoken)
#> {"sizes":{"ipad":{"h":313,"w":626,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/ipad"},"ipad_retina":{"h":626,"w":1252,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/ipad_retina"},"web":{"h":260,"w":520,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/web"},"web_retina":{"h":520,"w":1040,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/web_retina"},"mobile":{"h":160,"w":320,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/mobile"},"mobile_retina":{"h":320,"w":640,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/mobile_retina"},"300x100":{"h":100,"w":300,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/300x100"},"600x200":{"h":200,"w":600,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/600x200"},"1500x500":{"h":500,"w":1500,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/1500x500"},"1080x360":{"h":360,"w":1080,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/1080x360"}}}

:由于这是有效的,请尝试使用update_banner()

upload_banner <- function(banner_file, token) {
  query <- "account/update_profile_banner"

  banner_uri <- base64enc::base64encode(banner_file)

  url <- rtweet:::make_url(query = query,
                           param = list(banner = banner_uri))

  oauth <- oauth_POST(url, token)
  oauth <- oauth[names(oauth) == "Authorization"]

  req <- "--request POST 'https://api.twitter.com/1.1/account/update_profile_banner.json?'"

  header <- paste0("--header 'Authorization: ", oauth, "'")
  header2 <- paste0("--header 'Content-Type: application/json'")
  header3 <- paste0("--header 'Accept: application/json, text/xml, application/xml, */*'")
  data <- paste0('--data "banner=', banner_uri,'"')

  myargs <- paste(req, header2, header3, header, data, collapse = " ")
  system2("curl", args = myargs)
}

oauth_POST <- function (url = NULL, config = list(), ...,
                        body = NULL, encode = c("multipart", "form", "json", "raw"),
                        handle = NULL) {

  encode <- match.arg(encode)
  hu <- httr:::handle_url(handle, url, ...)
  req <- httr:::request_build("POST", hu$url, httr:::body_config(body, match.arg(encode)),
                      auth_req(config), ...)

  oauth_req(req, hu$handle$handle)
}

upload_banner(test_png, mytoken)
#>   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
#>                                 Dload  Upload   Total   Spent    Left  Speed
#> 100 16279  100    64  100 16215     97  24728 --:--:-- --:--:-- --:--:-- 25083
#> {"errors":[{"message":"Could not authenticate you","code":32}]}

在2022-06-16创建由 reprex package (v0.3.0)

由于这是失败的,我怀疑问题词干了从{httr}的授权中,我的猜测是长图像字符串是原因。


我尚未尝试的内容:

  • {rtweet}的开发版本:从我在github上看到的{httr}的呼叫均未发生什么变化,这就是为什么我希望问题持续存在的原因。<<<<<<<<<<<<<<<<< /p>

  • {httr2}软件包:我想我在某个地方读到它与oauth 1.0

  • 例如仅使用{curl}或{rcurl}或任何其他与R.Twitter API一起使用的软件包。


此端点仍在工作吗?

正如@LLRS指出的那样,用户端点位于迁移地图上,并将在Twitter的API 2.0中有所使用。但是,端点仍然在API 1.1下打开,我们可以使用滤纸代码更新Python中的配置文件横幅:

import os
import tweepy

# Get environment variables
CONKEY = os.getenv('MYTWITTER_CONSUMER_API_KEY')
CONSEC = os.environ.get('MYTWITTER_CONSUMER_API_SECRET')
ACCKEY = os.getenv('MYTWITTER_ACCESS_TOKEN')
ACCSEC = os.environ.get('MYTWITTER_ACCESS_TOKEN_SECRET')

auth = tweepy.OAuth1UserHandler(
   CONKEY,
   CONSEC,
   ACCKEY,
   ACCSEC
)

api = tweepy.API(auth)

image_url = 'https://raw.githubusercontent.com/TimTeaFan/dynamicTwitterHeader/main/data/test.png'

api.update_profile_banner(image_url)

I want to update my Twitter banner profle (the header image) using the Twitter API v1.1 from within R. I don’t care how it’s done, it can be a function from an existing R package (the ones I know of, do not have this kind of functionality) or it can be a call to system2("curl", args = args_strng) or any other approach - as long as it is done in R.

Below I will explain in detail what approaches I have tried so far.


Approach 1: Using {rtweet} (v0.7.0)

{rtweet} is the most popular R package used to interact with Twitter API v1.1. It comes with a lot of functions, but updating the banner profile is not supported yet.

The idea was create a function building on {rtweet} that allows linking a file path to an image of appropriate size (1500x500 px) to upload it as new Twitter banner.

update_profile_banner <- function (banner_file, token = NULL) {

  token <- rtweet:::check_token(token)

  banner_uri <- base64enc::base64encode(banner_file)

  query <- "account/update_profile_banner"

  # build params
  params <- list(banner = banner_uri)

  # make URL
  url <- rtweet:::make_url(query = query, param = params)

  # send
  r <- rtweet:::TWIT(get = FALSE, url, token)

  if (!r$status_code %in% c(200,201,202)) {
    return(httr::content(r))
  }

  message("your profile banner image has been updated!")
}

# Now all we need is a new banner image (see `test_png` below) and a valid Twitter token:

library(rtweet) # v.0.7.0
library(httr) # v.1.4.2

# Create a token containing Twitter keys
mytoken <- rtweet::create_token(
  app = "the name of the Twitter app", 
  consumer_key = Sys.getenv("MY_CONSUMER_API_KEY"),
  consumer_secret = Sys.getenv("MY_CONSUMER_API_SECRET"),
  access_token = Sys.getenv("MY_ACCESS_TOKEN"),
  access_secret = Sys.getenv("MY_ACCESS_TOKEN_SECRET"),
  set_renv = FALSE
)

test_png <- "https://raw.githubusercontent.com/TimTeaFan/dynamicTwitterHeader/main/data/test.png"

update_profile_banner(test_png, mytoken)
#> Error in curl::curl_fetch_memory(url, handle = handle): HTTP/2 stream 0 was not closed cleanly: INTERNAL_ERROR (err 2)

The error message isn’t very helpful but looking it up and changing HTTP version 2 to 1.1 throws a more meaningful message telling us that the header is too long.
I suspect that this is due to the fact that under the hood {httr} calls the following curl command:

POST https://api.twitter.com/1.1/account/update_profile_banner.json?banner=super_long_image_string_goes_here+oauth_credentials_come_last

It's just a simple POST command not differentiating between the header and the body, which (my guess) makes the header too long due to the image string.

To check whether this assumption is true (or at least not wrong), and to exclude the possibility that the approach itself is faulty, I created a similar function to get a banner image:

get_profile_banner <- function (screen_name, token) {

  token <- rtweet:::check_token(token)

  query <- "users/profile_banner"

  # build params
  params <- list(screen_name = screen_name)

  # make URL
  url <- rtweet:::make_url(query = query, param = params)
  r <- rtweet:::TWIT(get = TRUE, url, token)

    if (!r$status_code %in% c(200,201,202)) {
    return(httr::content(r))
  }

  rtweet:::from_js(r)
}

get_profile_banner("timteafan", mytoken)

#> $sizes
#> $sizes$ipad
#> $sizes$ipad$h
#> [1] 313
#> 
#> $sizes$ipad$w
#> [1] 626
#> 
#> $sizes$ipad$url
#> [1] "https://pbs.twimg.com/profile_banners/188839854/1653511325/ipad"
#> 
#> ...
#> [truncated]

Since get_profile_banner() is working fine, I suspect that the problem is indeed due to the long base64 encoded image string we are sending to the Twitter API in a simple call to curl POST.


Approach 2: Using system2("curl")

So a possible workaround might be to just call curl directly from R using system2() by building a more sophisticated call separating the header containing the authorization from the body containing the image string.

The only problem here is that we’d have to create our own signature. I tried this, but failed, so I decided to use {httr} to create the signature and all oauth credentials and then use those as header.

Let’s first start with get_banner_profile() to see whether this approach works.

For this we need some functions and helper functions:

This is the main function

get_banner <- function(screen_name, token) {

  query <- "users/profile_banner"

  url <- rtweet:::make_url(query = query,
                           param = list(screen_name = screen_name))

  # lets create the oauth credentials
  oauth <- oauth_GET(url, token)
  oauth <- oauth[[which(names(oauth) == "Authorization")]]

  # this is the initial curl command
  req <- "--get 'https://api.twitter.com/1.1/users/profile_banner.json?'"

  # this is the data
  data <- paste0("--data 'screen_name=", screen_name, "'")

  # and here goes the header
  header <- paste0("--header 'Authorization:", oauth, "'")

  # lets collapse everything into one string
  args <- paste(req, data, header, collapse = " ")
  system2("curl", args = args)
}

Some helper functions:

oauth_GET <- function (url = NULL, config = list(), ..., handle = NULL) {
  hu <- httr:::handle_url(handle, url, ...)
  req <- httr:::request_build("GET", hu$url, as.token.request(config), ...) 
  oauth_req(req, hu$handle$handle)
}

oauth_req <- function (req, handle) {
  stopifnot(httr:::is.request(req), inherits(handle, "curl_handle"))
  req <- httr:::request_prepare(req)
  req$headers
}

# httr methods
as.token.request <- function(x) auth_req(auth_token = x)

auth_req <- function (auth_token) {
  structure(list(auth_token = auth_token), class = "request")
}

library(httr)
get_banner("timteafan", mytoken)
#> {"sizes":{"ipad":{"h":313,"w":626,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/ipad"},"ipad_retina":{"h":626,"w":1252,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/ipad_retina"},"web":{"h":260,"w":520,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/web"},"web_retina":{"h":520,"w":1040,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/web_retina"},"mobile":{"h":160,"w":320,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/mobile"},"mobile_retina":{"h":320,"w":640,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/mobile_retina"},"300x100":{"h":100,"w":300,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/300x100"},"600x200":{"h":200,"w":600,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/600x200"},"1500x500":{"h":500,"w":1500,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/1500x500"},"1080x360":{"h":360,"w":1080,"url":"https:\/\/pbs.twimg.com\/profile_banners\/188839854\/1653511325\/1080x360"}}}

Since this is working, lets try the same with update_banner():

upload_banner <- function(banner_file, token) {
  query <- "account/update_profile_banner"

  banner_uri <- base64enc::base64encode(banner_file)

  url <- rtweet:::make_url(query = query,
                           param = list(banner = banner_uri))

  oauth <- oauth_POST(url, token)
  oauth <- oauth[names(oauth) == "Authorization"]

  req <- "--request POST 'https://api.twitter.com/1.1/account/update_profile_banner.json?'"

  header <- paste0("--header 'Authorization: ", oauth, "'")
  header2 <- paste0("--header 'Content-Type: application/json'")
  header3 <- paste0("--header 'Accept: application/json, text/xml, application/xml, */*'")
  data <- paste0('--data "banner=', banner_uri,'"')

  myargs <- paste(req, header2, header3, header, data, collapse = " ")
  system2("curl", args = myargs)
}

oauth_POST <- function (url = NULL, config = list(), ...,
                        body = NULL, encode = c("multipart", "form", "json", "raw"),
                        handle = NULL) {

  encode <- match.arg(encode)
  hu <- httr:::handle_url(handle, url, ...)
  req <- httr:::request_build("POST", hu$url, httr:::body_config(body, match.arg(encode)),
                      auth_req(config), ...)

  oauth_req(req, hu$handle$handle)
}

upload_banner(test_png, mytoken)
#>   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
#>                                 Dload  Upload   Total   Spent    Left  Speed
#> 100 16279  100    64  100 16215     97  24728 --:--:-- --:--:-- --:--:-- 25083
#> {"errors":[{"message":"Could not authenticate you","code":32}]}

Created on 2022-06-16 by the reprex package (v0.3.0)

Since this is failing, I suspect that the problem stems from the authorization via {httr} and my guess is that the long image string is the cause.


What I have not tried yet:

  • The development version of {rtweet}: From what I've seen on Github the underyling calls to {httr} haven't changed much, which is why I expect the problem to persist.

  • The {httr2} package: I think I read somewhere that it doesn't work with OAUTH 1.0

  • Other R packages such as using only {curl} or {Rcurl} or any other package that works with the Twitter API in R.


Is this endpoint still working?

As @llrs points out the user endpoints are on the migration map and will be available in Twitter's API 2.0 some time down the road. However, the endpoints are still open under API 1.1, we can use the folllowing code to update the profile banner in python:

import os
import tweepy

# Get environment variables
CONKEY = os.getenv('MYTWITTER_CONSUMER_API_KEY')
CONSEC = os.environ.get('MYTWITTER_CONSUMER_API_SECRET')
ACCKEY = os.getenv('MYTWITTER_ACCESS_TOKEN')
ACCSEC = os.environ.get('MYTWITTER_ACCESS_TOKEN_SECRET')

auth = tweepy.OAuth1UserHandler(
   CONKEY,
   CONSEC,
   ACCKEY,
   ACCSEC
)

api = tweepy.API(auth)

image_url = 'https://raw.githubusercontent.com/TimTeaFan/dynamicTwitterHeader/main/data/test.png'

api.update_profile_banner(image_url)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文