(2023/05/16) コメントにて、「GASからの初回認証時に『ReferenceError: OAuth2 is not defined』エラーになる」というご指摘をいただきました。こちら、GAS側でライブラリを追加していないと発生するエラーです。ライブラリ追加に関する記載が抜けていたので追加しました。ご指摘ありがとうございました。
(2023/10/13) コメントにて、認証に失敗するというご指摘をいただきました。CLIENT_ID、CLIENT_SECRETを発行する手順について変更・追記しました。ご指摘ありがとうございました。
本記事では、Google Apps Script(GAS)からTwitterのAPI経由でツイッター(現X)に投稿する方法を紹介します。
↑のツイッターアカウントでは、ブログの更新をした際に手動でURLを貼り付けてお知らせをしているのですが、どうしても忘れてしまいます。そこで、ブログ更新のお知らせを自動で投稿できるようにしたいと考えました。とはいえ、最初から色々と機能を盛り込むのも大変なので、まずはAPI経由でツイートするところまでを作成します。
Twitter APIは、3/29に新しいAPIが発表になりましたが、本記事は新しいAPI(Twitter API v2)のFreeプランで動かしています。新しいTwitter APIに関する詳細は以下をご参照ください。
Twitter側の設定
Twitter APIの登録
まず、Twitter Developer Platformを開き、ご自身のTwitterアカウントでログインします。
Twitterアプリケーションの作成
ログインすると以下の画面が開きますので、右上の「Create an App」を押します。
「Apply」を押します。
「Ready to build on Twitter?」という画面が開きます。
(まだの場合のみ)電話番号認証
Twitter APIの利用には電話番号認証が必要となりますので、まだの方は「Verify Phone」を押下して電話番号認証を完了させてください。
電話番号は、最初の0を取った番号を入力します。「080-1111-2222」なら「8011112222」となります。
「Next」を押すと、入力した電話番号にショートメールで認証コードが届きますので、次のページで入力してください。入力後、自分のツイッターの画面に遷移すれば認証完了です。
完了したら、ページを更新(F5キーまたは、ブラウザの左上の更新アイコンを押下)します。すると、先ほど非活性だったボタンが押せるようになっているはずです。
無料/有料プランを選択
画面の一番下にある「Sign up for Free Account」を押下します。有料プランを登録する場合には「Subscribe」から登録します。
利用目的を記載して利用開始
「Developer agreement & policy」という画面では、Twitter APIの利用目的を記載します。250字以上入力する必要がありますが、審査されるわけではないので内容はなんでもOKです。
…といっても、250字って意外と長いです。DeepLなどの翻訳ツールで適当に申請内容っぽい文章を作ると楽です。
「Submit」を押下すると、Dashboard画面に遷移します。
【変更につき本手順は以降で行う】Client ID・Client Secretの取得
左側メニューから、Default project-XXXの下にある、TwitterIDから始まる項目を押下します。
上部のタブから「Keys and tokens」を選択します。
「OAuth 2.0 Client ID and Client Secret」の「Client ID」と「Client Secret」をメモしておきます。(あとでGASのスクリプトに記載します)
Twitter APIの設定
上部のタブから「Settings」を選択します。
「User authentication settings」の「Edit」を押下します。
App permissions
App permissions(required)は「Read and write」を選択します。
Type of App
「Type of App(required)」は「Web App, Automated App or Bot」を選択します。
App info
「App info」は「Callback URI / Redirect URL (required) 」にGASのコールバックURLを設定します。こちらは次章(すぐ下)で詳しく解説しています。
「Website URL (required)」については、そのURLが実在するかどうかまではチェックしていないようなので(参考)、今回は自分のツイッターURLを設定しておきました。
Google Apps Scriptのプロジェクトを作成
Google Apps Script(GAS)を開いて、新しいプロジェクトを作成します。
ここで、コールバックURLを取得しておきます。新規作成したGASのプロジェクトのURLのID部分をコピーして、以下のようにURLを生成します。
https://script.google.com/home/projects/[ここをコピー]/edit
↓
https://script.google.com/macros/d/[コピーしたやつを貼り付け]/usercallback
こちらのコールバックURLを、Twitter APIのApp info>Callback URI / Redirect URL (required) に記載してください。
Client ID・Client Secretの取得
App InfoのWebsite URLに↑を貼り付けたら、「Save」を押します。
「Changing permissions might affect your app」と出ますので、「Yes」を押します。
「OAuth 2.0 Client ID and Client Secret」の「Client ID」と「Client Secret」をメモしておきます。(あとでGASのスクリプトに記載します)
OAuth2ライブラリの追加
左側メニューから「ライブラリ」を押します。
「スクリプトID」を入力します。
こちらをコピーしてそのまま貼り付けて「検索」を押し、OAuth2が出てきたら「追加」を押します。
Twitter API認証処理
GASのエディタを開くとデフォルトで作成されている「myFunction」は全て削除し、Twiter APIの認証処理を記載します。
参考:https://twittercommunity.com/t/cannot-auth-my-google-script-app/177367
https://github.com/googleworkspace/apps-script-oauth2
const CLIENT_ID = 'XXX'
const CLIENT_SECRET = 'XXX'
function getService() {
pkceChallengeVerifier();
const userProps = PropertiesService.getUserProperties();
const scriptProps = PropertiesService.getScriptProperties();
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(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.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(CLIENT_ID + ':' + CLIENT_SECRET),
'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();
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());
}
CLIENT_ID、CLIENT_SECRET には、Twitter APIの「Client ID・Client Secretの取得」の部分で説明したとおり、「OAuth 2.0 Client ID and Client Secret」の「Client ID」と「Client Secret」を記載してください。
初回実行時は認証が必要
Twitter APIで初回実行する際には、認証が必要です。以下のコードを先ほどのスクリプトに追加します。
追加場所はどこでも大丈夫ですが、const変数の宣言の下(3行目以降)あたりに入れておけば良いかと思います。
参照:https://qiita.com/yuzinet/items/ae4b9ca2b5cd989de435
function main() {
const service = getService();
if (service.hasAccess()) {
Logger.log("Already authorized");
} else {
const authorizationUrl = service.getAuthorizationUrl();
Logger.log('Open the following URL and re-run the script: %s', authorizationUrl);
}
}
実行する関数が「main」になっていることを確認して、「実行」を押します。
「承認が必要です」というダイアログが出ますので、「権限を承認」を押します。
「アプリにアクセスを許可」を押します。
「Success!」と表示されたら認証完了です。
承認に成功したら、上記の画面は閉じてしまってOKです。
ツイート処理の実装
ツイート部分の処理を追加します。
参考:https://twittercommunity.com/t/cannot-auth-my-google-script-app/177367
https://qiita.com/yuzinet/items/ae4b9ca2b5cd989de435
function sendTweet() {
var payload = {
text: 'Test tweet from API!!!!!'
}
var service = getService();
if (service.hasAccess()) {
var url = `https://api.twitter.com/2/tweets`;
var response = UrlFetchApp.fetch(url, {
method: 'POST',
'contentType': 'application/json',
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
},
muteHttpExceptions: true,
payload: JSON.stringify(payload)
});
var result = JSON.parse(response.getContentText());
Logger.log(JSON.stringify(result, null, 2));
} else {
var authorizationUrl = service.getAuthorizationUrl();
Logger.log('Open the following URL and re-run the script: %s',authorizationUrl);
}
}
実行する関数が「sendTweet」になっていることを確認して、「実行」を押します。実行結果にはツイートID、ツイートしたテキストが出力されます。
ツイッターを確認すると、設定した内容でツイートされていることを確認できました。
注意点
API経由でツイッター投稿をする際の注意点は以下になります。
過去のツイート内容と同じものはツイートできない
Twitter API経由で、過去にツイートした内容と一言一句同じ内容はツイートできません。
対策としては、過去の投稿を削除するか、ツイート内容を一部分でも変更する必要があります。同じような内容を定期的にツイートしたい場合には、日付や管理番号などを付加すると良いかと思います。
無料版の投稿上限は1500回/月まで
Twitter社は3/29に新しいAPIを発表しました。無料版でツイートできる回数は、一か月に1500回までとなります。月額100ドルのプランでは、一か月に3000回までです。この回数は、ツイートだけでなくDMやリツイート、お気に入り登録なども1回としてカウントされますので、無料版では全然足りないどころか、有料版でも運用が難しいとの声が見受けられます。
今後30日間のあいだに新しいAPIに移行するよう呼び掛けており、その後は従来のAPIは廃止になるとのことです。
参考にさせていただいたサイト
本記事を作成するにあたり、参考にさせていただいたサイトは以下になります。
・GASでTwitterのAPIを使って投稿(ツイート)する
認証まわりのスクリプトについて、解説が分かりやすかったです。
・OAuth2、API v2でTwitter🐦にツイートできるようにしてみた(GAS)
無駄のない分かりやすい解説でした。初回認証で躓いたので大変参考になりました。
・Twitter Developersのフォーラム
サンプルコードを参考にしました。
・Apps Script OAuth2 のreadme
GASからOAuth2を利用する際の基本的な流れが書いてあります。
まとめ
今回は、Twitter APIとGASを利用してツイートをする方法について解説しました。私自身、TwitterのAPIを利用するのは初めてでしたので、既に使い込んでいる主人(prtn)に聞きながら、また、ネットの記事を参考にしながらなんとかツイートすることができました。
今後は、Twitter APIを利用して、ブログを更新したらお知らせツイートをしたり、GASのトリガー(定期実行)を使って過去の記事を紹介するツイートなどができたらいいなと思っています。
コメント
はじめまして!
GAS初心者の松田と申します。
このサイトに掲載のスクリプトをコピーしてテストしてみたのですが、認証用のmain()関数の実行時に下記のエラーになりました。
エラー内容
ReferenceError: OAuth2 is not defined
getService @Tweet_bot.gs:8
sendTweet @ninshou.gs:2
Tweet_bot.gsの8行目は以下になります。
return OAuth2.createService(‘twitter’)
サイト掲載のコード自体は何も変えてないのですが・・・
はじめまして!コメントありがとうございます。
記載いただいたエラーの原因は、CLIENT_ID、CLIENT_SECRETの中身を設定していないことが原因かと思います。
サンプルコードは、
const CLIENT_ID = ‘XXX’
const CLIENT_SECRET = ‘XXX’
となっていますが、これをご自身の環境に合わせて変更していただく必要があります。
こちらは、「Client ID・Client Secretの取得」の章にて取得方法を解説していますので、取得した値を入れて実行していただくと
正しく動くかと思います。
また試してみて不明点等ございましたらお気軽にコメントいただければと思います。
この通りにやったらできました!!ありがとうございます♀️♀️♀️
す さん
コメントありがとうございます。
お力になれてとても嬉しいです!
ブログ更新の励みになります♪
はじめまして
参考にさせていただいております
お書き頂いている通りに行っているのですが、mainで発行されたURLでアプリの許可までは行くのですが『アプリにアクセスを許可』を押下するとローディングの後にエラーでBadRequestと言われてしまいます
どうすればいいでしょうか?
お手数ですがご教授いただけると幸いです
はじめまして、コメントありがとうございます。
BadRequestと出ている場合、クライアント(こちら側)のエラーだと思われます。
・「Client ID」と「Client Secret」が正しく記載されているか
・発行されたURLを正しくコピペして認証ページを開いているか
上記を試して解決しなければ、ブラウザのキャッシュ・cookieを削除してみてください。
もし、ブラウザに拡張機能を入れている場合、メモリ不足を起こしている可能性がありますので一旦無効にしてみてください。
こちら側で再現できないため、的確な返答ができていなかったらすみません。。
解決しない場合はまたご相談いただければと思います。
はじめまして、こちらの記事を参考にさせていただき無事にTwitter(X)への自動投稿に成功いたしました。誠にありがとうございます!
自動投稿には成功したのですが、数日経つとmain()を実行し認証?をしないと動かなくなってしまいました。
認証は定期的に手動で行う必要があるのでしょうか?
また、authCallbackを実行すると以下のようなエラーが発生していました。
これが原因なのかな?と考えたのですが解決できず…
もし何かご存知でしたらご教授いただければ幸いです。
TypeError: Cannot read properties of undefined (reading ‘parameter’)
Service_.handleCallback @ Service.gs:440
authCallback @ コード.gs:38
はじめまして、ブログを閲覧いただき誠にありがとうございます!
GAS側でOAuth2ライブラリは追加されていますでしょうか?
エラーが出ている箇所ではアクセス権限の認可を行っており、こちらがOAuth2のライブラリ関数となっています。
そのため、ライブラリを予めGAS内で追加していないとエラーになります。
本記事では「OAuth2ライブラリの追加」にて解説しています。
もし、すでにライブラリの追加が完了していたら見当外れなご指摘となってしまい申し訳ございません。
私自身もこちらのスクリプトでXへの投稿をしていますが、認証は初回時のみで、それ以降は必要ありません。
また分からない点があればお気軽にコメントいただければと思います。
はじめまして。コメント失礼します。
関数「main」を実行すると以下のようなエラーが出てしまいます。
どうか、ご教授お願いします。
エラー:
TypeError: redeclaration of const CLIENT_SECRET.
コメントありがとうございます。
「const CLIENT_SECRET = ‘XXXXXXXXXXXXXXXXXXXXXXXXXXX’」を複数宣言していないでしょうか?
エラー内容的に、CLIENT_SECRETという変数を再宣言しているというエラーのようです。
何度も確認しましたが、CLIENT_SECRET を宣言しているのは冒頭の1か所のみです。
環境周りを整備して再度「main」を実行すると以下のログが出力されます。
「アプリにアクセスを許可」ページには遷移しません。
Open the following URL and re-run the script: https://twitter.com/i/oauth2/authorize?client_id=UHhMRU03MjU1dmhwQmRhMEJjUUQ6MTpjaQ&response_type=code&redirect_uri=https%3A%2F%2Fscript.google.com%2Fmacros%2Fd%2F17s2Nuk02RQDNw79HZHXQ7f-ycdd9aGKS8JAGBdma3Clc-fRAK4uzswTc%2Fusercallback&state=ADEpC8yVlm-j16N1TNMsc8lTxiCtCXEqn8_LPR9mQ68x4LSgcll9OpwCTlMqCTcD4QIFmmffVBduQXdBASwJ6IxCPmY9tfDGr9W9ttIMy6o9z_d7lMP-JoCiQN-v7om3pAke2eOE9bN1wO1Q3shGjAKOUowlKR40CxJhqwu-VjqdlSy-bA_mycM&scope=users.read%20tweet.read%20tweet.write%20offline.access&code_challenge_method=S256&code_challenge=5gHmgQIoeGuH7Ekm6pNaYcS4XfxeJRoMdD6nnY1WpI4
上記のURLにアクセスすると、Twitterの簡易画面になり、次のようなメッセージが出ます。
“問題が発生しました
アプリにアクセスを許可できません。前に戻ってもう一度ログインしてください。”
スクリプトを再実行しても同じ結果となります。
見当違いのコメント、失礼いたしました。
詳細な内容をありがとうございます。
メッセージ内容をもとに調べてみましたが、Client ID・Client Secretが正しく設定されていないことが原因だと書かれている記事がありました。
再度、本記事の「Client ID・Client Secretの取得」の手順を確認していただけますでしょうか。
よろしくお願いいたします。
はじめまして。質問失礼いたします。
記事を参考にさせていただき設定を進めましたが、
認証の部分がうまくいかず躓いています。
「○○がXアカウントへのアクセスを求めています.」➡【アプリにアクセスを許可】
の青ボタンを押下するところまではいくのですが、
そのあと少しロードを挟んだのち、真っ白な画面の左上に
「{“message”:”認証に失敗しました。”}」
と出てしまいます。
Client ID・Client Secretは最新のものを使用し、
入力できているはずなのですが・・・
なにか対策があればご教示いただけますと幸いです。
コメントありがとうございます。
認証まで進んでいて失敗しているというケースは経験がなく、有効な対策が浮かばないのが正直なところです。
お力になれず申し訳ございません・・・。
この記事を書いたのがXに移行する前でしたので、もしかしたら記事の内容では現在できないことも考えられます。
一度、こちらでも設定しなおしてみて出来るか試してみます。
何かわかりましたらまたこちらでコメントしますね。
よろしくお願いいたします!
早速のお返事ありがとうございます!!
お忙しい中誠に恐れ入ります・・・
なにか進捗がありましたら、お返事お待ちしております
追加で必要な情報(状況・条件など)がございましたら、
お返事にて補足させていただきますのでお気軽にお申し付けください。
よろしくお願いいたします。
chimaki 様
ご連絡が遅くなってしまい、申し訳ございません。
こちら側で、別のアカウントで一から試してみたのですがどうやらブログを書いた当時と手順が(操作する順番)が変わっているようでした。
具体的には、CLIENT_IDとCLIENT_SECRETの発行タイミングが記事内容と異なるみたいです・・・
当方の環境では、記事に記載の手順で自動ツイートができるところまで確認済です。
記事を修正しましたので、記載の順番で再度試してみていただけますでしょうか。
また何か、不明点などございましたらお気軽にコメントいただければと思います。
こちらこそよろしくお願いいたします!
chaso 様
先日は早速の検証・ご確認誠にありがとうございました!
お返事が遅くなり申し訳ございません。
その後、修正確認していただいた内容で何度か試してみたのですが、
やはり状況変わらず「{“message”:”認証に失敗しました。”}」
と出てしまいました(汗)
当方のシステム上の問題かもしれませんので、折を見て一から確認してみようと思います。
お忙しい中、迅速なご対応に感謝いたします。
また、とても分かりやすく参考になる記事をありがとうございます。
今後も応援しております^^!
chimaki様
ご連絡、ありがとうございます。
だめでしたか・・・>< お役に立てず申し訳ございません。。もし、他にアカウントをお持ちでしたら 別のアカウントで試してみてどのような状況になるか見ていただくと原因の切り分けができるかなと思います。 また何かありましたらお気軽にコメントいただければと思います。 こちらこそ、わざわざご連絡いただきありがとうございました(^^)
mainを実行して表示されたURLに進んでも「問題が発生しました」でここから先に進めません
色々調べてClient ID・Client Secretの設定がうまく言ってない場合に起こるということがわかったのですが双方発行されたものをコピペしているので間違いは無いと思うのですが・・・。
一体なぜなのでしょうか?
コメントいただきましてありがとうございます。
少し前に、同様の内容で他の方からもコメントをいただき、
こちらで再度一連の流れを設定してみたのですが、問題なく自動ツイートすることができました。
もし、別のアカウントで試せるのであれば、別のアカウントでもエラーになるかどうか試していただけますでしょうか。
現状、原因がこちらとしても分かっておらず、参考になる回答ができず申し訳ございません。
よろしくお願いいたします。
はじめまして。こちらの記事、非常に助かりました。
他の方と似たように認証後BadRequestになりましたが、Cookieをバッサリ削除したら通りました。
Google系のCookieをガッツリ削りました。
はじめまして、コメントありがとうございます。
お役に立てて嬉しいです!
Cookieがエラーの一因だったんですね、有益な情報ありがとうございます。
記事にも追記しておきます。
初めまして、参考にさせていただいております、ありがとうございます。
sendTweet()を実行すると、以下のエラーが発生して詰まっております。
何か原因等考えられることがありましたら、ご教示いただけると助かります。
“`
{
“title”: “Forbidden”,
“type”: “about:blank”,
“status”: 403,
“detail”: “Forbidden”
}
“`
ちなみにmain()はすでに実行して、「Success!」の表示が出ることは確認しております。
また、 console.log(service.getAccessToken()) にて、アクセストークン取得ができていることも確認しております。
当方では、課金しておらず Freeプランです。
403になっている原因や調査の方向性など何でも構いませんので、アドバイスいただけると助かります。
コメントありがとうございます。
403エラーはアクセス権限がない場合などに出ますが、
考えられる原因として、
・複数アカウントをお持ちで、実際にツイートしたいアカウントと異なるアカウントに対して設定をしている
・X(Twitter)のアカウント自体に制限がかかっている
・対象のアカウントに何等かの問題がある→別アカウントで試してみる
上記くらいしか思いつきません。申し訳ございません・・・。
自分自身も本記事の方法にて自動ツイートできており、
403エラーが発生したことがないため適切なアドバイスができずすみません。
また何か新しい情報等ありましたら、共有していただけますと幸いです。
よろしくお願いいたします。