【Twitter】Twitter(X)自動投稿の設定(2024年版) - Google Apps Scriptを使った方法
Twitterの自動投稿を設定する方法について、初心者の方にもわかりやすく解説していきます。Twitter API v2(X API Free)を使用して、スプレッドシートから投稿内容を取得して自動投稿を行う手順を順を追って説明していきます。
Twitter Developer Portalに登録する
まずは、Twitter Developer Portalに登録する必要があります。
- Twitter Developer Portalにアクセスします。
- 右上の「Developer Portal(開発者ポータル)」クリックします。

- Twitterアカウントでログインしていない場合は、ログインします。

- 「Sign up for Free Account」を選択します。

- 必要事項を英語で入力します。使用目的などを簡潔に説明してください。

- 利用規約に同意し、「Submit」をクリックします。
アプリを作成し、APIキーを取得する
Developer Portalに登録できたら、次はアプリを作成してAPIキーを取得します。
-
Developer Portalのダッシュボードから、自動生成されたプロジェクトとアプリを確認します。
-
左側メニューからあなたのアプリ名をクリックします。

- You understand that you may not resell anything you receive via the Twitter APIs
Twitter APIを通じて入手した情報やデータを、他人に売ることは禁止されています。この規則を理解し、守ることに同意します。 - You understand your Developer account may be terminated if you violate the Developer Agreement or any of the Incorporated Developer Terms
開発者向けの規約やルールに違反した場合、あなたの開発者アカウントが停止または削除される可能性があることを理解し、同意します。 - You accept the Terms & Conditions
Twitter(X)の開発者向けサービスを利用するにあたり、すべての利用規約を読み、その内容を理解し、従うことに同意します。
- You understand that you may not resell anything you receive via the Twitter APIs
-
「User authentication settings」の「Set up」をクリックします。

-
以下の設定を行います。

- App permissions: Read and write
- Type of App: Web App, Automated App or Bot
- Callback URI / Redirect URL: (後述の手順で取得します)
https://script.google.com/macros/d/[YOUR_SCRIPT_ID]/usercallback注意:
[YOUR_SCRIPT_ID]は後述のGASのスクリプトIDに置き換えます。- Website URL: あなたのウェブサイトURL(なければTwitterのURLでも可)
-
設定を保存します。
-
「Keys and tokens」タブから、以下のキーを取得し、安全な場所に保存します。

- Client ID
- Client Secret
GASでコールバックURLを取得する
-
Google ドライブにアクセスし、新しいGoogle スプレッドシートを作成します。
-
スプレッドシートを開き、「拡張機能」>「Apps Script」を選択します。
-
Apps Scriptプロジェクトが開いたら、「デプロイ」>「新しいデプロイ」をクリックします。

-
「種類の選択」ドロップダウンから「ウェブアプリ」を選択します。

-
以下の設定を行います。

- 説明:任意の説明を入力(例:「Twitter自動投稿アプリ」)
- 次のユーザーとして実行:自分(あなたのメールアドレス)
- アクセスできるユーザー:全員
-
「デプロイ」ボタンをクリックします。
-
デプロイが完了すると、「ウェブアプリのURL」が表示されます。
-
このURLから以下の形式でコールバックURLを作成します:
https://script.google.com/macros/d/[YOUR_SCRIPT_ID]/usercallback注意:
[YOUR_SCRIPT_ID]は、ウェブアプリのURLに含まれるスクリプトIDに置き換えます。 -
作成したコールバックURLをTwitter Developer Portalの「Callback URI / Redirect URL」設定欄に入力します。
GASにOAuth2ライブラリを追加する
スクリプトを使用する前に、OAuth2ライブラリをプロジェクトに追加する必要があります。
-
Google Apps Scriptのプロジェクト画面を開きます。

-
左側のメニューから「ライブラリ +」をクリックします。

-
「スクリプトID」の入力欄に以下のIDを貼り付けます。
1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF -
「検索」ボタンをクリックします。

-
表示されたライブラリ(“OAuth2”という名前のはずです)を選択します。
-
バージョンは最新のものを選択します。
-
「追加」ボタンをクリックして、ライブラリをプロジェクトに追加します。

スプレッドシートの準備
Googleスプレッドシートに投稿内容をリストとして準備します。
-
スプレッドシートに以下のような構造でデータを入力します:
- A列: ツイートのID(ユニークな識別子)
- B列: ツイート内容
- C列: 投稿回数(最初は
0としておきます) - D列: 最終投稿日時
ID ツイート内容 投稿回数 最終投稿日時 1 これは最初のツイートです。 0 2 こちらは二つ目のツイートです。 0
GASでTwitter投稿スクリプトを作成する
OAuth2ライブラリを追加したら、スプレッドシートからランダムにツイートを取得し、投稿するスクリプトを作成します。以下のコードを貼り付けます。
-
GASのエディタを開きます。
-
以下のスクリプトをコピーして貼り付けます。
const TWITTER_CLIENT_ID = 'YOUR_CLIENT_ID'; const TWITTER_CLIENT_SECRET = 'YOUR_CLIENT_SECRET'; const TWITTER_SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID'; function getServiceTwitter() { const pkce = pkceChallengeVerifier(); return OAuth2.createService('twitter') .setAuthorizationBaseUrl('https://twitter.com/i/oauth2/authorize') .setTokenUrl('https://api.twitter.com/2/oauth2/token') .setClientId(TWITTER_CLIENT_ID) .setClientSecret(TWITTER_CLIENT_SECRET) .setCallbackFunction('doGet') .setPropertyStore(PropertiesService.getUserProperties()) .setScope('tweet.read tweet.write users.read offline.access') .setParam('response_type', 'code') .setParam('code_challenge_method', 'S256') .setParam('code_challenge', pkce.challenge) .setTokenHeaders({ 'Authorization': 'Basic ' + Utilities.base64Encode(TWITTER_CLIENT_ID + ':' + TWITTER_CLIENT_SECRET), 'Content-Type': 'application/x-www-form-urlencoded' }) .setTokenPayloadHandler(function(tokenPayload) { tokenPayload.code_verifier = pkce.verifier; return tokenPayload; }); } function pkceChallengeVerifier() { var userProps = PropertiesService.getUserProperties(); var verifier = userProps.getProperty("code_verifier"); var challenge = userProps.getProperty("code_challenge"); if (!verifier || !challenge) { verifier = generateCodeVerifier(); challenge = generateCodeChallenge(verifier); userProps.setProperty("code_verifier", verifier); userProps.setProperty("code_challenge", challenge); } console.log('PKCE Verifier (from pkceChallengeVerifier): ' + verifier); console.log('PKCE Challenge (from pkceChallengeVerifier): ' + challenge); return { verifier: verifier, challenge: challenge }; } function generateCodeVerifier() { var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"; var verifier = ""; for (var i = 0; i < 128; i++) { verifier += possible.charAt(Math.floor(Math.random() * possible.length)); } return verifier; } function generateCodeChallenge(verifier) { var sha256Hash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, verifier); return Utilities.base64Encode(sha256Hash) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); } function doGet(e) { const service = getServiceTwitter(); if (e.parameter.code) { const authorized = service.handleCallback(e); if (authorized) { return HtmlService.createHtmlOutput('認証成功!このタブを閉じてください。'); } else { return HtmlService.createHtmlOutput('認証失敗。このタブを閉じてください。'); } } else { const authorizationUrl = service.getAuthorizationUrl(); return HtmlService.createHtmlOutput('<a href="' + authorizationUrl + '">Twitterで認証する</a>'); } } function postTweet(status) { const service = getServiceTwitter(); if (service.hasAccess()) { const url = 'https://api.twitter.com/2/tweets'; const payload = { 'text': status }; const options = { 'method': 'POST', 'headers': { 'Authorization': 'Bearer ' + service.getAccessToken(), 'Content-Type': 'application/json' }, 'payload': JSON.stringify(payload), 'muteHttpExceptions': true }; const response = UrlFetchApp.fetch(url, options); console.log(response.getContentText()); } else { console.log('認証が必要です'); } } function postRandomTweet() { const sheet = SpreadsheetApp.openById(TWITTER_SPREADSHEET_ID).getActiveSheet(); const data = sheet.getDataRange().getValues(); let candidate = null; let rowIndex = -1; for (let i = 0; i <= 100; i++) { const filteredData = data.filter((row, index) => (Number(row[2]) || 0) === i); if (filteredData.length > 0) { const randomIndex = Math.floor(Math.random() * filteredData.length); candidate = filteredData[randomIndex]; rowIndex = data.findIndex(row => row[0] === candidate[0]); break; } } if (!candidate) { console.log('No candidates found'); return; } postTweet(candidate[1]); sheet.getRange(rowIndex + 1, 3).setValue((Number(candidate[2]) || 0) + 1); sheet.getRange(rowIndex + 1, 4).setValue(new Date()); } function resetAuthAndGetNewUrl() { PropertiesService.getUserProperties().deleteAllProperties(); const pkce = pkceChallengeVerifier(); const service = getServiceTwitter(); service.reset(); return service.getAuthorizationUrl(); } function debugPKCE() { const userProps = PropertiesService.getUserProperties(); const verifier = userProps.getProperty("code_verifier"); const challenge = userProps.getProperty("code_challenge"); console.log('Stored Code Verifier: ' + verifier); console.log('Stored Code Challenge: ' + challenge); console.log('Regenerated Challenge: ' + generateCodeChallenge(verifier)); } function resetAuthAndGetNewUrl() { PropertiesService.getUserProperties().deleteAllProperties(); const pkce = pkceChallengeVerifier(); const service = getServiceTwitter(); service.reset(); const newAuthUrl = service.getAuthorizationUrl(); console.log('New Auth URL: ' + newAuthUrl); console.log('PKCE Verifier (from reset): ' + pkce.verifier); console.log('PKCE Challenge (from reset): ' + pkce.challenge); return newAuthUrl; } function clearProperties() { PropertiesService.getUserProperties().deleteAllProperties(); console.log('All properties cleared'); } function debugStoredPKCE() { const userProps = PropertiesService.getUserProperties(); console.log('Stored Verifier: ' + userProps.getProperty("code_verifier")); console.log('Stored Challenge: ' + userProps.getProperty("code_challenge")); } function debugAuth() { const userProps = PropertiesService.getUserProperties(); console.log('Code Verifier: ' + userProps.getProperty("code_verifier")); console.log('Code Challenge: ' + userProps.getProperty("code_challenge")); } -
YOUR_CLIENT_IDとYOUR_CLIENT_SECRETを、Twitter Developer Portalで取得したキーに置き換えます。
スクリプトを実行し、認証を行う
- スクリプトを保存し、新しくデプロイします。
resetAuthAndGetNewUrl関数を実行します。
- 権限の確認画面が出た場合には”無題のプロジェクトに移動”に進みます。

- ログに表示される認証URLをコピーし、ブラウザで開きます。

- Twitterの認証画面が表示されるので、アプリを認証します。

- 認証が完了したら、
postRandomTweet関数を実行してツイートを投稿します。

自動投稿のスケジュールを設定する
- GASエディタで「トリガー」を選択します。

- 「トリガーを追加」をクリックします。

- 関数に
postRandomTweetを選択し、イベントのソースを「時間主導型」に設定します。
好みの頻度(例:12時間おき)を設定し、保存します。
以上で、Twitterの自動投稿の設定が完了です!設定した頻度で自動的にツイートが投稿されるようになります。
注意点
- Twitter APIの利用制限に注意しましょう。無料プランでは1日50件までの投稿制限があります。
- 投稿内容は適切なものを心がけ、スパムと判断されないよう注意しましょう。
- 定期的にスクリプトの動作を確認し、必要に応じて調整を行いましょう。