如何使用 OAuth2 库从 Google App Script 发送推文?

How can I tweet from Google App Script with OAuth2 library?

我已经使用 OAuth1 成功地为推文创建了 GAS。众所周知,OAuth1 GAS 库已被弃用,因此我正在尝试迁移到 OAuth2 库。

我看到了一些变化,但我没有得到正确的方法来授权我的请求。

我现在的主要问题是:

为了更加清晰,我将代码从 Google 开发者网站中提取,并根据我的建议进行了调整:

// Call this function just once, to initialize the OAuth client.
function initializeOAuthClient() {
  if (typeof OAuth2 === 'undefined') {
    var libUrl = 'https://developers.google.com/google-ads/scripts/docs/examples/oauth20-library';
    throw Error('OAuth2 library not found. Please take a copy of the OAuth2 ' +
        'library from ' + libUrl + ' and append to the bottom of this script.');
  }
  var tokenUrl = 'https://api.twitter.com/oauth2/token';
  authUrlFetch = OAuth2.withClientCredentials(
      tokenUrl, CONSUMER_KEY, CONSUMER_SECRET);
}

function sendTweet(status) {
  var service = accessProtectedResource(SERVICE_UPDATE_URL, "post");
  
  if (service.hasAccess()) {
    var url = 'https://api.twitter.com/1.1/statuses/update.json?include_entities=true&status=' + percentEncode(status);
    var response = service.fetch(url);
    //var result = JSON.parse(response.getContentText());
    
    return response;
  }
}


/**
 * Attempts to access a non-Google API using a constructed service
 * object.
 *
 * If your add-on needs access to non-Google APIs that require OAuth,
 * you need to implement this method. You can use the OAuth1 and
 * OAuth2 Apps Script libraries to help implement it.
 *
 * @param {String} url         The URL to access.
 * @param {String} method_opt  The HTTP method. Defaults to GET.
 * @param {Object} headers_opt The HTTP headers. Defaults to an empty
 *                             object. The Authorization field is added
 *                             to the headers in this method.
 * @return {HttpResponse} the result from the UrlFetchApp.fetch() call.
 */
function accessProtectedResource(url, method_opt, headers_opt) {
  var service = getOAuthService();
  var maybeAuthorized = service.hasAccess();
  if (maybeAuthorized) {
    // A token is present, but it may be expired or invalid. Make a
    // request and check the response code to be sure.

    // Make the UrlFetch request and return the result.
    var accessToken = service.getAccessToken();
    var method = method_opt || 'post';
    var headers = headers_opt || {};
    headers['Authorization'] =
        Utilities.formatString('Bearer %s', accessToken);
    var resp = UrlFetchApp.fetch(url, {
      'headers': headers,
      'method' : method,
      'muteHttpExceptions': true, // Prevents thrown HTTP exceptions.
    });

    var code = resp.getResponseCode();
    if (code >= 200 && code < 300) {
      return resp.getContentText("utf-8"); // Success
    } else if (code == 401 || code == 403) {
       // Not fully authorized for this action.
       maybeAuthorized = false;
    } else {
       // Handle other response codes by logging them and throwing an
       // exception.
       console.error("Backend server error (%s): %s", code.toString(),
                     resp.getContentText("utf-8"));
       throw ("Backend server error: " + code);
    }
  }

  if (!maybeAuthorized) {
    // Invoke the authorization flow using the default authorization
    // prompt card.
    CardService.newAuthorizationException()
        .setAuthorizationUrl(service.getAuthorizationUrl())
        .setResourceDisplayName("Display name to show to the user")
        .throwException();
  }
}

/**
 * Create a new OAuth service to facilitate accessing an API.
 * This example assumes there is a single service that the add-on needs to
 * access. Its name is used when persisting the authorized token, so ensure
 * it is unique within the scope of the property store. You must set the
 * client secret and client ID, which are obtained when registering your
 * add-on with the API.
 *
 * See the Apps Script OAuth2 Library documentation for more
 * information:
 *   https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
 *
 *  @return A configured OAuth2 service object.
 */
function getOAuthService() {
  
  return OAuth2.createService('SERVICE_NAME')
      .setAuthorizationBaseUrl('SERVICE_AUTH_URL')
      .setTokenUrl('SERVICE_AUTH_TOKEN_URL')
      .setClientId('CLIENT_ID')
      .setClientSecret('CLIENT_SECRET')
      .setScope('SERVICE_SCOPE_REQUESTS')
      .setCallbackFunction('authCallback')
      .setCache(CacheService.getUserCache())
      .setPropertyStore(PropertiesService.getScriptProperties())
}

/**
 * Boilerplate code to determine if a request is authorized and returns
 * a corresponding HTML message. When the user completes the OAuth2 flow
 * on the service provider's website, this function is invoked from the
 * service. In order for authorization to succeed you must make sure that
 * the service knows how to call this function by setting the correct
 * redirect URL.
 *
 * The redirect URL to enter is:
 * https://script.google.com/macros/d/<Apps Script ID>/usercallback
 *
 * See the Apps Script OAuth2 Library documentation for more
 * information:
 *   https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
 *
 *  @param {Object} callbackRequest The request data received from the
 *                  callback function. Pass it to the service's
 *                  handleCallback() method to complete the
 *                  authorization process.
 *  @return {HtmlOutput} a success or denied HTML message to display to
 *          the user. Also sets a timer to close the window
 *          automatically.
 */
function authCallback(callbackRequest) {
  var authorized = getOAuthService().handleCallback(callbackRequest);
  if (authorized) {
    return HtmlService.createHtmlOutput(
      'Success! <script>setTimeout(function() { top.window.close() }, 1);</script>');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}

/**
 * Unauthorizes the non-Google service. This is useful for OAuth
 * development/testing.  Run this method (Run > resetOAuth in the script
 * editor) to reset OAuth to re-prompt the user for OAuth.
 */
function resetOAuth() {
  getOAuthService().reset();
}

function main() {
  try {
      let result = sendTweet("Este va a ser un gran día!\n https://www.instagram.com/amos_oficialba/");
      Logger.log("Resultado: " + result);
  }
  catch(err) {
    console.log(err["stack"]);
  }
}

本机支持已从 OAuthConfig 中删除,但这不会阻止您的应用向外部 API 发出 OAuth 1 请求。开源库 OAuth1 for Apps Script 是作为替代品创建的,以防您之前使用 OAuthConfig

使用 OAuth1 for Apps 脚本库从 Google Apps 脚本发推文:

  1. 您需要在 Twitter 开发者门户中设置回调 URL。使用此库时,回调 URL 将始终采用 https://script.google.com/macros/s/YOUR_SCRIPT_ID/usercallback 格式。您需要将 YOUR_SCRIPT_ID 替换为您脚本的 ID。
  2. 在 Google Apps 脚本中,转到文件菜单和 select 项目属性。记下您的脚本 ID。
  3. Twitter Developer portal、select 您的应用程序中,然后单击身份验证设置下的编辑。
  4. 添加回调 URL,完成后点击保存:
  5. 返回 Google Apps 脚本,select 资源菜单,然后单击库。
  6. 在库 window 中,输入 OAuth1 库的 ID 1CXDCY5sqT9ph64fFwSzVtXnbjpSfWdRymafDrtIZ7Z_hwysTY7IIhi7s,然后单击添加。
  7. Select 最新版本(18 在撰写本文时)

完成后,使用此脚本设置有效的推文请求。将 CONSUMER_KEYCONSUMER_SECRET 替换为您应用的 API 密钥和密码;将 TOKENTOKEN_SECRET 替换为您用户的访问令牌和访问令牌密码。

var CONSUMER_KEY = 'your consumer key';
var CONSUMER_SECRET = 'your consumer secret';
var TOKEN = 'your access token';
var TOKEN_SECRET = 'your access token secret';
/**
 * Authorizes and makes a request to the Twitter API.
 */
function run() {
  var service = getService();
  Logger.log(service.getCallbackUrl())
  if (service.hasAccess()) {
    var url = 'https://api.twitter.com/1.1/statuses/update.json';
    var payload = {
      status: 'just setting up my google apps script'
    };
    var response = service.fetch(url, {
      method: 'post',
      payload: payload
    });
    var result = JSON.parse(response.getContentText());
    Logger.log(JSON.stringify(result, null, 2));
  } else {
    var authorizationUrl = service.authorize();
    Logger.log('Open the following URL and re-run the script: %s',
        authorizationUrl);
  }
} 

function doGet() {
  return HtmlService.createHtmlOutput(ScriptApp.getService().getUrl());
}

/**
 * Reset the authorization state, so that it can be re-tested.
 */
function reset() {
  var service = getService();
  service.reset();
}

/**
 * Configures the service.
 */
function getService() {
  return OAuth1.createService('Twitter')
      // Set the endpoint URLs.
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authorize')

      // Set the consumer key and secret.
      .setConsumerKey(CONSUMER_KEY)
      .setConsumerSecret(CONSUMER_SECRET)

      // Set your user's access token key and secret
      .setAccessToken(TOKEN, TOKEN_SECRET)

      .setCallbackFunction('authCallback')  
}

/**
 * Handles the OAuth callback.
 */
function authCallback(request) {
  var service = getService();
  var authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('Success!');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}

或者,您可以使用 Google 自己的 OAuth 1 replacement script to sign OAuth 1 requests. You can find an example of usage in the Google Ads script page