LoginSignup
9
0

X(Twitter)投稿自動化の記事を書いた振り返り

Last updated at Posted at 2023-12-01

概要

先日、X(Twitter)を投稿するのをGASで自動化した記事を執筆したのですが、追加で書けてなかったハマりどころポイントとQiitaを書いたことで色々とフィードバックいただけのが嬉しかったのでこの年末にまとめとして書かせていただきます。

執筆後のフィードバック

タイポを修正してもらいました。

@sinfuk1997 さん修正リクエストありがとうございました。

image.png

というか、Qiitaで編集リクエストなんて出来ることを知れたのが凄く良い収穫でした。

コメントいただきました

@studiosimply さん
@Michio029 さん
コメントありがとうございました。励みになります!

自動化した後にハマったポイント(チーム開発だとなるかも)

原因

GASのプロジェクトを自動化してX(Twitter)に投稿できるようになったー!!
と思っていたのですが、いざ運用に乗せるとトリガーで実行したジョブは「Not Authorised」となり運用が開始できてなかったです。

これは、以前のQiita記事を書いた後に発覚したことです。

原因は、チームでプロジェクトを運用していたために、GASのスクリプトを自分自身で実行すると問題ないのですがトリガーを利用して実行すると実行ユーザーが変わることで環境変数が利用できず認証時に格納している
認証情報にアクアセスできず認証が突破できない事象です。

原因特定までの紆余曲折

この事象を調べてみると色々と参考になりそうな記事が。。。

と思ったら私が執筆したQiitaの記事を参照して実装してくれている記事が結構出てくる!!
(地味に嬉しい!!)
ですが同じ問題に当たっている人には遭遇できず。。。

原因特定

Google Apps ScriptのPropertiesServiceにはスコープがありこれが実行ユーザーによって利用できるものと出来ないことがあることが分かりました。

image.png

この、PropertiesService.getUserProperties() を利用していたことでプロジェクトの実行時(トリガー)を使って自動実行した場合にその作成ユーザーで実行されてしまってことが原因でした。

対処方法

PropertiesServiceの格納場所を変更することで対処しました。
UserPropertiesからDocumentPropertiesに変更することで対処しました。

対処時のスクリプト
function getService() {
  pkceChallengeVerifier();
-  const userProps = PropertiesService.getUserProperties();
+  const userProps = PropertiesService.getDocumentProperties();
  const scriptProps = PropertiesService.getScriptProperties();
  const clientId = scriptProps.getProperty('CLIENT_ID');
  const clientSecret = scriptProps.getProperty('CLIENT_SECRET');

  return OAuth2.createService('twitter')
    .setAuthorizationBaseUrl('https://twitter.com/i/oauth2/authorize')
    .setTokenUrl('https://api.twitter.com/2/oauth2/token?code_verifier=' + userProps.getProperty("code_verifier"))
    .setClientId(clientId)
    .setClientSecret(clientSecret)
    .setCallbackFunction('authCallback')
    .setPropertyStore(userProps)
    .setScope('users.read tweet.read tweet.write offline.access')
    .setParam('response_type', 'code')
    .setParam('code_challenge_method', 'S256')
    .setParam('code_challenge', userProps.getProperty("code_challenge"))
    .setTokenHeaders({
      'Authorization': 'Basic ' + Utilities.base64Encode(clientId + ':' + clientSecret),
      'Content-Type': 'application/x-www-form-urlencoded'
    })
}

function authCallback(request) {
  const service = getService();
  const authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('Success!');
  } else {
    return HtmlService.createHtmlOutput('Denied.');
  }
}

function pkceChallengeVerifier() {
-  var userProps = PropertiesService.getUserProperties();
+  var userProps = PropertiesService.getDocumentProperties;
  if (!userProps.getProperty("code_verifier")) {
    var verifier = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";

    for (var i = 0; i < 128; i++) {
      verifier += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    var sha256Hash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, verifier)

    var challenge = Utilities.base64Encode(sha256Hash)
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '')
    userProps.setProperty("code_verifier", verifier)
    userProps.setProperty("code_challenge", challenge)
  }
}

function logRedirectUri() {
  var service = getService();
  Logger.log(service.getRedirectUri());
}

まとめ

今年は、Qiitaの記事を昨年に比べて多く書きました。(それでも5記事くらい。)
記事を執筆したことで修正リクエストやコメントなど色々と反応頂けて嬉しかったです!
また、実際に利用して実装してくれていた方がいたのがもっと嬉しかった!
少しでも誰かの為になっていたら嬉しいです

来年も、備忘を兼ねた記事を執筆していこうと思います。

それでは、良いお年を〜!

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0