【Threads】Threads自動投稿の設定(2024年版) - Google Apps Scriptを使った方法
PUBLISHED 2024-10-17
-
Google スプレッドシートを新規作成
- 新しいスプレッドシートを作成します。
- スプレッドシートの名前を任意の名前(Threads Contentなど)に変更します。

-
Apps Script の設定
- 「拡張機能」メニューから「Apps Script」を選択します。

- 新しいスクリプトファイルが開きます。以下のコードを貼り付けてください:
const THREADS_CLIENT_ID = 'YOUR_CLIENT_ID'; const THREADS_CLIENT_SECRET = 'YOUR_CLIENT_SECRET'; const THREADS_SCOPE = 'threads_basic,threads_content_publish'; const THREADS_AUTH_URL = 'https://threads.net/oauth/authorize'; const THREADS_TOKEN_URL = 'https://graph.threads.net/oauth/access_token'; const THREADS_SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID'; function getServiceThreads() { return OAuth2.createService('threads') .setAuthorizationBaseUrl(THREADS_AUTH_URL) .setTokenUrl(THREADS_TOKEN_URL) .setClientId(THREADS_CLIENT_ID) .setClientSecret(THREADS_CLIENT_SECRET) .setCallbackFunction('authCallback') .setPropertyStore(PropertiesService.getUserProperties()) .setScope(THREADS_SCOPE) .setParam('access_type', 'offline') .setParam('prompt', 'consent') .setTokenHeaders({ 'Authorization': 'Basic ' + Utilities.base64Encode(THREADS_CLIENT_ID + ':' + THREADS_CLIENT_SECRET) }); } function authCallback(request) { var service = getServiceThreads(); var isAuthorized = service.handleCallback(request); if (isAuthorized) { return HtmlService.createHtmlOutput('認証成功!このウィンドウを閉じて、スクリプトエディタに戻ってください。'); } else { return HtmlService.createHtmlOutput('認証に失敗しました。もう一度お試しください。'); } } function getAuthorizationUrl() { var service = getServiceThreads(); if (!service.hasAccess()) { var authorizationUrl = service.getAuthorizationUrl(); Logger.log('以下のURLにアクセスして認証を行ってください:'); Logger.log(authorizationUrl); return authorizationUrl; } else { Logger.log('すでに認証されています。'); return null; } } function getThreadsToken() { var service = getServiceThreads(); if (service.hasAccess()) { var accessToken = service.getAccessToken(); Logger.log('Access Token: ' + accessToken); var response = UrlFetchApp.fetch( 'https://graph.threads.net/me?fields=id', { headers: { Authorization: 'Bearer ' + accessToken } } ); var userId = JSON.parse(response.getContentText()).id; Logger.log('User ID: ' + userId); return { access_token: accessToken, user_id: userId }; } else { Logger.log('認証が必要です。getAuthorizationUrl()を実行してください。'); return null; } } function postToThreads() { if (!checkAndRefreshToken()) { Logger.log('トークンの更新に失敗しました。再認証が必要です。'); return; } var tokenData = getThreadsToken(); if (!tokenData) { Logger.log('トークンの取得に失敗しました。認証を確認してください。'); return; } const CREATE_URL = `https://graph.threads.net/v1.0/${tokenData.user_id}/threads`; const PUBLISH_URL = `https://graph.threads.net/v1.0/${tokenData.user_id}/threads_publish`; const sheet = SpreadsheetApp.openById(THREADS_SPREADSHEET_ID).getActiveSheet(); const data = sheet.getDataRange().getValues(); let candidates = []; let minPostCount = Infinity; for (let i = 0; i < data.length; i++) { if (i === 0 && (data[i][0] === 'ID' || data[i][1] === '投稿内容')) { continue; } let postCount = data[i][2] || 0; if (typeof postCount === 'string') postCount = parseInt(postCount, 10) || 0; if (postCount <= minPostCount) { if (postCount < minPostCount) { candidates = []; minPostCount = postCount; } candidates.push({ content: data[i][1], rowIndex: i }); } } if (candidates.length === 0) { Logger.log('投稿する内容がありません。'); return; } const selected = candidates[Math.floor(Math.random() * candidates.length)]; const content = selected.content; const createOptions = { method: "post", headers: { "Authorization": "Bearer " + tokenData.access_token, "Content-Type": "application/x-www-form-urlencoded" }, payload: { text: content, media_type: "TEXT", access_token: tokenData.access_token } }; try { const createResponse = UrlFetchApp.fetch(CREATE_URL, createOptions); if (createResponse.getResponseCode() === 200) { const creationId = JSON.parse(createResponse.getContentText()).id; const publishOptions = { method: "post", headers: { "Authorization": "Bearer " + tokenData.access_token, "Content-Type": "application/x-www-form-urlencoded" }, payload: { creation_id: creationId, access_token: tokenData.access_token } }; const publishResponse = UrlFetchApp.fetch(PUBLISH_URL, publishOptions); if (publishResponse.getResponseCode() === 200) { Logger.log("投稿成功: " + content); let currentCount = sheet.getRange(selected.rowIndex + 1, 3).getValue() || 0; sheet.getRange(selected.rowIndex + 1, 3).setValue(currentCount + 1); sheet.getRange(selected.rowIndex + 1, 4).setValue(new Date()); } else { Logger.log("投稿の公開に失敗: " + content + ", ステータスコード: " + publishResponse.getResponseCode()); Logger.log("レスポンス: " + publishResponse.getContentText()); } } else { Logger.log("スレッドの作成に失敗: " + content + ", ステータスコード: " + createResponse.getResponseCode()); Logger.log("レスポンス: " + createResponse.getContentText()); } } catch (error) { Logger.log("エラー発生: " + error); } } function checkAndRefreshToken() { var service = getServiceThreads(); if (service.hasAccess()) { Logger.log('トークンは有効です。'); return true; } else { Logger.log('トークンが無効または期限切れです。更新を試みます。'); try { if (service.refresh()) { Logger.log('トークンを更新しました。'); return true; } else { Logger.log('トークンの更新に失敗しました。再認証が必要です。'); return false; } } catch (e) { Logger.log('エラーが発生しました: ' + e.toString()); Logger.log('再認証が必要です。getAuthorizationUrl()を実行してください。'); return false; } } } function clearStoredToken() { PropertiesService.getUserProperties().deleteProperty('oauth2.threads'); Logger.log('保存されていたトークンをクリアしました。'); } - 「拡張機能」メニューから「Apps Script」を選択します。
-
スクリプトのデプロイ
- 「デプロイ」→「新しいデプロイ」をクリックし、ウェブアプリとしてデプロイします。

- 「種類の選択」で「ウェブアプリ」を選択します。

- 「次のユーザーとして実行」で「自分」を選択します。
- 「アクセスできるユーザー」で「全員」を選択します。

- 「デプロイ」→「新しいデプロイ」をクリックし、ウェブアプリとしてデプロイします。
OAuth2 ライブラリの追加
- ライブラリの追加
-
左側のメニューから「ライブラリ +」をクリックし、以下のIDを追加します。
-
「スクリプトID」の入力欄に以下のIDを貼り付けます。
1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF -
「検索」ボタンをクリックします。

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

-
Meta for Developers の設定
-
Meta for Developers アカウントの設定
- https://developers.facebook.com/ にアクセスし、マイアプリをクリックします。

- Metaのデベロッパーアカウントを作成していない場合、メールアドレスを入力して下記の画面に進みます。

- facebookアカウントにブラウザでログインします。
- 基本データ > 連絡先情報からメールアドレスの登録を行います。
- SMSの認証画面が表示されるのでコードを入力します。

- 成功すると、アカウントに追加画面が表示されます。

- 元の画面に戻り、メールアドレスに送信されたコードを入力します。

- アカウント作成画面が表示されるので、適当なものを選択します。

- マイアプリ画面が表示されます。

- https://developers.facebook.com/ にアクセスし、マイアプリをクリックします。
-
ThreadsのアクセストークンとユーザーIDを取得:
- デベロッパーダッシュボードで「アプリを作成」をクリックします。

- ビジネスポートフォリオを選択します。

- Threads APIにアクセス を選択します。

- アプリ名、連絡先を入力します。

- 利用規約、ポリシーを確認し、アプリを作成をクリックします。

- パスワードを入力します。facebookのログインパスワードで進むことができました。

- アプリが作成されました と表示されるので、ダッシュボードにアクセスをクリックします。

- 左メニューの「アプリの設定」→「ベーシック」から、ThreadsアプリIDとアプリシークレットをメモします。

- デベロッパーダッシュボードで「アプリを作成」をクリックします。
-
アプリの設定:
- threads_content_publish の追加クリックします。

- 左メニューの「アプリの設定」→「ベーシック」を開きます。
- ダッシュボードのアクセス許可から Threads API にアクセスをクリックします。

- ユーザートークン生成ツール から、テスターを追加します。

- 「テスターを追加」をクリックし、自分のThreadsアカウントを追加します。
ユーザーIDを入力して、アカウントを探すことができます。

- Threads にアクセスし、ログインを行います。
- 左下の設定をクリックします。

- アカウントの招待から同意するをクリックします。

- threads_content_publish の追加クリックします。
-
アプリの詳細設定
- アプリの設定ページで、以下の項目を設定します:
- コールバックURLリダイレクト
- コールバックURLをアンインストール
- コールバックURLを削除
これらに以下のURLを入力します。
[YOUR_SCRIPT_ID]は実際のスクリプトIDに置き換えてください。
https://script.google.com/macros/d/[YOUR_SCRIPT_ID]/usercallback(https://script.google.com/macros/d/[YOUR_SCRIPT_ID]/usercallback)
Script IDは、Apps Scriptのエディタ画面のURLから確認できます。

- アプリの設定ページで、以下の項目を設定します:
認証プロセスとアクセストークンの取得
-
認証URLの生成
- スクリプト内の
CLIENT_IDとCLIENT_SECRETを、取得したアプリIDとアプリシークレットに置き換えます。 getAuthorizationUrl()関数を実行し、コンソールに表示されたURLをブラウザで開きます。
- 権限を確認が表示された場合は、詳細から移動をクリックし、許可を行います。(信頼できる場合のみ移動を押すようにしてください。)

- 認証を行うと、GASのウェブアプリケーションにリダイレクトされ、認証が完了します。
- コンソールに表示されたURLをブラウザで開きます。

- 認証を行うと、GASのウェブアプリケーションにリダイレクトされます。


下記のように表示された場合、callback関数に正しくApps ScriptのIDを入力し、設定できていない可能性があります。
{ "error_message": "URLはブロックされています: リダイレクトURIがアプリのクライアントOAuth設定でホワイトリストに追加されていないため、リダイレクトできませんでした。クライアントとウェブOAuthログインをオンにして、すべてのアプリドメインを有効なOAuthリダイレクトURIとして追加してください。", "error_code": 1349168 } - スクリプト内の
-
アクセストークンとユーザーIDの取得
- 認証後、
getThreadsToken()関数を実行してアクセストークンとユーザーIDを取得します。


- 認証後、
スプレッドシートIDの設定と投稿機能の準備
-
スプレッドシートIDの設定
-
スプレッドシートを開き、URLからスプレッドシートIDを取得します。
URLは以下のような形式です。https://docs.google.com/spreadsheets/d/[SPREADSHEET_ID]/edit#gid=0 -
スクリプト内の
THREADS_SPREADSHEET_IDの値を、取得したIDで置き換えます。
const THREADS_SPREADSHEET_ID = 'スプレッドシートID'; -
-
スプレッドシートの準備
- スプレッドシートの1行目に以下のヘッダーを設定します。
A列: ID, B列: 投稿内容, C列: 投稿回数, D列: 最終投稿日時 - B列に投稿したい内容を入力します。
- スプレッドシートの1行目に以下のヘッダーを設定します。
-
投稿機能のテスト
- スクリプトエディタで
postToThreads()関数を実行します。 - 実行結果をログで確認し、正常に投稿されたか確認します。

- スプレッドシートを確認し、投稿回数と最終投稿日時が更新されているか確認します。
- スクリプトエディタで
定期実行の設定
- トリガーの設定
- スクリプトエディタの左サイドバーから「トリガー」アイコンをクリックします。
- 「トリガーを追加」ボタンをクリックします。
- 以下の設定を行います。
- 実行する関数を選択:
postToThreads - イベントのソースを選択:
時間主導型 - 時間ベースのトリガーのタイプを選択:
時間ベースのタイマー - 時間の間隔を選択: 希望の間隔(例:1時間ごと)
- 実行する関数を選択:
- 「保存」をクリックします。

運用とメンテナンス
-
定期的なチェック
- スプレッドシートの内容が定期的に投稿されているか確認します。
- エラーログがないか、スクリプトエディタの実行ログを確認します。
-
トークンの更新
- アクセストークンは60日で期限切れになります。
- 60日ごとに
getAuthorizationUrl()とgetThreadsToken()を再実行して、トークンを更新します。
-
コンテンツの管理
- 定期的にスプレッドシートに新しい投稿内容を追加します。
- 投稿済みの内容を確認し、必要に応じて削除や更新を行います。
注意点
- Threadsのコンテンツポリシーを遵守してください。
- 投稿頻度が高すぎると、アカウントがスパム扱いされる可能性があります。適切な間隔を設定してください。
- Google Apps Scriptの実行制限(1日あたりのクォータ)に注意してください。
これらの手順に従うことで、Google スプレッドシートとGoogle Apps Scriptを使用して、Threadsへの自動投稿システムを構築・運用することができます。定期的なメンテナンスと監視を行い、システムが正常に機能し続けるようにしてください。