ソーシャル投稿
Richfolio は買いシグナルを公開ソーシャルアカウント — X/Twitter、Facebook ページ、Threads アカウント、LinkedIn ページ — に、メール+Telegram ブリーフと併せてオプションで投稿できます。動作するのはdaily と intraday モードのみです(weekly や refresh では決して投稿しません)。
これは完全にオプトインです。ソーシャルの認証情報を設定しなければ何も投稿されず、Richfolio の他の機能はこれまでどおり動作します — 各プラットフォームは「credentials not set — skipping」という行をログに残して次へ進みます。
投稿される内容
投稿は意図的に汎用的な内容にしてあり、プライベートな情報は一切漏れません:
- STRONG BUY と BUY のシグナルのみ(HOLD/WAIT はスキップ)。
- 各シグナルにはティッカー、アクション、確信度、短い理由、そしてオプションでバリュー評価が表示されます — それ以外は何もありません。
- ターゲットポートフォリオと観察リストのティッカーは一律に「シグナル」としてまとめられます — 「ポートフォリオ vs 観察リスト」というラベルはないため、保有銘柄が明かされることは決してありません。
- 決して投稿されないもの: 配分、ギャップ、推奨買い付け額、株数、ポートフォリオ総額。
src/socialContent.ts内のbuildSignalLines()が唯一の許可リストのチョークポイントであり、sanitizeReason()が AI の理由に書き込まれうる配分ギャップ、ドル建ての金額、重複割引のテキストを除去します。どちらもユニットテストでカバーされています。 - ティッカーは Facebook / Threads / LinkedIn ではクリック可能な
#hashtagとして描画されます(キャッシュタグは X でのみ機能し、そこでは$cashtagのまま残ります)。X 以外のプラットフォームでは、リーチを高めるために設定可能な汎用ハッシュタグのセットが追加されます。 - すべての投稿は次の免責事項で終わります:「Not financial advice. Auto-generated by Richfolio.(投資助言ではありません。Richfolio により自動生成されています。)」
有効化/無効化
config.json 内の social ブロックがマスタースイッチです(デフォルト値を表示):
"social": {
"enabled": true,
"includeLinkInX": false,
"hashtags": ["investing", "stocks", "stockmarket", "ETFs"]
}
enabled: false— 認証情報の有無にかかわらず、すべてのソーシャル投稿を無効化します。includeLinkInX— X の投稿に分析リンクを含めます。リンクを含めると X の従量課金コストが上がるため、デフォルトではオフです。hashtags— Facebook / Threads / LinkedIn で追加される汎用ハッシュタグ(先頭の#は省略可能)。X では追加されません。
各プラットフォームはさらに自身の認証情報でゲートされるため、enabled が true でもキーが設定されていないプラットフォームはスキップされます。
Secret 一覧
これらをリポジトリの Secret(Settings → Secrets and variables → Actions → Secrets)として追加します。すべてオプションです — 利用したいプラットフォームのみ設定してください。
| Secret | プラットフォーム | 注記 |
|---|---|---|
FACEBOOK_PAGE_ID | ページの数値 id | |
FACEBOOK_PAGE_TOKEN | 長期有効なページアクセストークン | |
THREADS_USER_ID | Threads | Threads の数値ユーザー id |
THREADS_ACCESS_TOKEN | Threads | 長期有効なトークン(約 60 日で失効) |
THREADS_TOKEN_PAT | Threads | Threads トークンを自動リフレッシュするためのオプションの PAT(下記参照) |
LINKEDIN_ACCESS_TOKEN | w_organization_social を持つ OAuth 2.0 トークン | |
LINKEDIN_ORG_URN | 例:urn:li:organization:123456 | |
X_API_KEY / X_API_SECRET | X/Twitter | OAuth 1.0a コンシューマキー |
X_ACCESS_TOKEN / X_ACCESS_TOKEN_SECRET | X/Twitter | OAuth 1.0a ユーザートークン |
Facebook ページのセットアップ
ランタイムが必要とするのは 2 つの値だけです:FACEBOOK_PAGE_ID と長期有効な FACEBOOK_PAGE_TOKEN。自分のページに管理者として投稿するため、アプリの開発モードのままで構いません — アプリレビューは不要です。
1. Meta アプリを作成
- developers.facebook.com → My Apps → Create App に移動。
- Other → Business のアプリタイプを選び、名前を付けます(例:「Richfolio Poster」)。
2. ページ管理のユースケースを追加
- アプリで Use cases を開き、Manage everything on your Page を選択。
- その Permissions を開いて Add します(Standard Access で十分です — 「Get Advanced Access」は無視してください):
pages_manage_posts— フィードへの投稿(必須のもの)pages_read_engagementpages_show_list
これらは 「Ready for testing」 と表示されるはずです — これは管理者であるあなたが今すぐ利用できることを意味します。
3. ユーザートークンを取得
- Graph API Explorer を開きます。
- アプリを選択 → Get User Access Token → 3 つの
pages_*権限にチェック → Generate Access Token → 承認。
4. 長期有効なページトークンを発行
Richfolio にはトークン交換を代行するヘルパーが付属しています。一時的に .env に追加してください:
FACEBOOK_PAGE_ID=your_page_id # numeric Page id
FACEBOOK_APP_SECRET=... # App settings → Basic → App secret
FB_USER_TOKEN=... # the user token from step 3
そして実行します:
npx tsx smoke/fb-page-token.ts
これは短期有効なユーザートークンを長期有効なものと交換し、あなたのページを見つけて、有効期限のないページトークンを出力します。それを FACEBOOK_PAGE_TOKEN に貼り付け、その後 .env から FACEBOOK_APP_SECRET と FB_USER_TOKEN を削除してください — これらはセットアップ専用で、ランタイムでは決して使われません。
5. 検証
npx tsx smoke/smoke-facebook.ts # checks the token (no posting)
npx tsx smoke/smoke-facebook.ts --post --cleanup # posts a test, then deletes it
PASS が出れば、トークンが有効なページトークンであり、投稿がエンドツーエンドで動作することが確認できます。
6. GitHub に追加
FACEBOOK_PAGE_ID と FACEBOOK_PAGE_TOKEN をリポジトリの Secret として追加します。アプリシークレットやユーザートークンは追加しないでください。
トークンの寿命: ページトークンは事実上恒久的ですが、Facebook のパスワードを変更したり、アプリを取り消したり、ページ管理者でなくなったりすると失効します。投稿が止まった場合は、ステップ 3 から再発行してください。
Threads のセットアップ
Threads は Facebook と同じ Meta アプリを再利用します。THREADS_USER_ID と長期有効な THREADS_ACCESS_TOKEN が必要です。認可するアカウントが投稿元として表示されるアカウントになります — あなたのブランド用 Threads アカウントを使ってください。アカウントは公開である必要があります。
1. Threads のユースケースを追加 — Meta アプリで Use cases → Access the Threads API。スコープ threads_basic と threads_content_publish を有効にします。
2. 自分を Threads Tester として追加 — App Dashboard → App roles → Roles → Add People → Threads Tester → あなたの Threads ユーザー名を入力 → Threads アプリで招待を承認(Settings → Account → Website permissions)。
3. 長期有効なトークンを生成 — Threads ユースケースの Settings にある User Token Generator にあなたのテスターアカウントが一覧表示されます → Generate token。これはすでに長期有効です — 交換は不要です。.env に THREADS_ACCESS_TOKEN として貼り付けてください。
4. ユーザー id を取得して検証:
npx tsx smoke/smoke-threads.ts # prints THREADS_USER_ID; set it in .env
npx tsx smoke/smoke-threads.ts --post --cleanup # posts a test (delete may be manual)
5. THREADS_USER_ID と THREADS_ACCESS_TOKEN を GitHub Secret に追加します。
トークンの失効と自動リフレッシュ: Threads の長期有効トークンは約 60 日で失効します。ワークフロー
.github/workflows/refresh-threads-token.ymlが毎月トークンをリフレッシュし、Secret に書き戻します — ただしTHREADS_TOKEN_PATSecret(リポジトリの Secrets: Read and write 権限を持つ fine-grained PAT)も追加した場合に限ります。その PAT がなければ、失効前に手動でトークンをリフレッシュしてください。
LinkedIn ページのセットアップ
LinkedIn は無料ですが、最も制限が厳しいです。必要なもの:
- あなたの会社ページに紐付いた LinkedIn Developer アプリ。
- 「Community Management API」 プロダクト。これが
w_organization_socialスコープを付与します。これをリクエストするには、アプリの会社紐付けが認証済みである必要があり(Settings タブ)、アクセスフォームの提出が必要です(登録済みの事業者名を求められます)。 - ページの管理者によって生成された、
w_organization_socialを持つ OAuth 2.0 アクセストークン。
設定:
LINKEDIN_ACCESS_TOKEN— OAuth 2.0 トークン。LINKEDIN_ORG_URN— あなたの organization URN、例:urn:li:organization:123456(数字は会社ページの URL にあります)。LINKEDIN_API_VERSION— オプションの上書き(デフォルトは直近のYYYYMM)。
npx tsx smoke/smoke-linkedin.ts(トークンチェック)と --post --cleanup(投稿テスト)で検証します。
LinkedIn のアクセストークンは約 60 日で期限切れになります(リフレッシュトークンは約 365 日有効)。定期的にリフレッシュする計画を立ててください。組織への投稿には登録済みの事業者が必要です。お持ちでない場合は、セルフサービスの「Share on LinkedIn」プロダクト(
w_member_social)を使って個人プロフィールから投稿するようにポスターを書き換えることもできます。
X / Twitter のセットアップ
X には 2026 年 2 月以降、無料の投稿枠がありません。 投稿は従量課金です(1 投稿あたり約 $0.015、リンクを含むとさらに高くなります)。Richfolio には X ポスターが付属していますが、キーを追加するまでは休止状態のままです。
投稿は OAuth 1.0a ユーザーコンテキストを使用します。X Developer Portal で Read and Write 権限のプロジェクト/アプリを作成し、以下を生成します:
X_API_KEY/X_API_SECRET— コンシューマ(API)キーとシークレット。X_ACCESS_TOKEN/X_ACCESS_TOKEN_SECRET— あなたのアカウントのアクセストークンとシークレット。
1 投稿あたりのコストを抑えるため、config.json の includeLinkInX はオフのままにしてください。
動作の仕組み
daily と intraday モードでは、メールと Telegram の送信後に、Richfolio が sendSocialPosts()(src/social.ts)を呼び出します。各プラットフォームは自身の try/catch の中で投稿するため、あるプラットフォームの失敗が他のプラットフォーム — あるいはすでに送信済みのメール/Telegram — をブロックすることは決してありません。
Richfolio をフォークした場合、上記のセットアップを自分自身のアカウントで完了するまで、ソーシャル投稿はオフのままです — 各トークンはその所有者のアカウントに投稿するため、認証情報を共有することはできません。