YAML で定義したポスト/スレッドを、X (Twitter) に対して指定時刻に投稿します。GitHub Actions の定期実行を想定しています。
スレッド投稿、画像(最大4件)、動画付き投稿、各画像への代替テキスト設定などにも対応します。
git clone https://github.com/burnworks/x-scheduled-posts
cd x-scheduled-posts
npm install
data/tweets.sample.yamlをコピーしdata/tweets.yamlにリネームして内容を編集- GitHub リポジトリの Secrets に環境変数を設定
.github/workflows/scheduled-post.ymlの cron を調整(必要に応じて / 初期値は15分毎実行)- GitHub Actions を有効化してスケジュール実行
ローカルで動作確認したい場合は .env を作成し(.env.example をコピーしてください)、API 利用に必要な環境変数を設定した上で、以下を実行します(投稿せずログだけ出します)。
DRY_RUN=true node src/index.js --file data/tweets.yaml
.env.example を参考に各値を正確に設定してください。
X_CLIENT_IDX_CLIENT_SECRETX_ACCESS_TOKENX_REFRESH_TOKEN: (任意)あれば自動更新を試みます
OAuth1.0a の利用が必要な場合は以下をすべて設定してください。OAuth1.0a と OAuth2 のどちらも設定されていた場合、OAuth1.0a を使用します。
X_API_KEYX_API_KEY_SECRETX_ACCESS_TOKENX_ACCESS_TOKEN_SECRET
DRY_RUN:trueor1を指定すると投稿せずログだけ出します。falseや0を設定する、あるいはこの変数自体を省略すればfalse扱いとなります。
ただし、DRY_RUNを実行する場合でも、API に関連する環境変数について正しいものが設定されている前提です。
単一のポストを指定日時で投稿したい場合は、以下のように tweets 配下に各ポストを配置します。
timezone: "Asia/Tokyo"
tweets:
- number: 1
schedule_at: "2026-02-02T13:00"
text: |
予約投稿のテストです。
改行もそのまま反映されます。
https://example.com
images:
- path: "../assets/images/one.png"
alt: "画像の alt テキスト"
- path: "../assets/images/two.jpg"
- number: 2
schedule_at: "2026-02-02T13:20"
text: "投稿テキスト"
- number: 3
schedule_at: "2026-02-02T16:00"
text: |
予約投稿テキスト
#hashtag
images:
- path: "../assets/images/three.png"
alt: "画像の alt テキスト"
- number: 4
schedule_at: "2026-02-03T09:30"
text: "予約投稿テキスト"
video: "../assets/videos/demo.mp4"
スレッド投稿をしたい場合は、thread を使用してポストをネストします。
timezone: "Asia/Tokyo"
tweets:
- number: 1
schedule_at: "2026-02-02T13:00"
text: |
予約投稿のテストです。
改行もそのまま反映されます。
https://example.com
images:
- path: "../assets/images/one.png"
alt: "画像の alt テキスト"
- path: "../assets/images/two.jpg"
thread:
- number: 2
schedule_at: "2026-02-02T13:20"
text: "「number: 1」に対する返信として投稿されます。"
thread:
- number: 3
text: |
「number: 2」に対する返信として投稿されます。
schedule_at を省略すると親の日時を継承します。
images:
- path: "../assets/images/three.png"
alt: "画像の alt テキスト"
- number: 4
schedule_at: "2026-02-03T09:30"
text: "別の予約投稿。"
video: "../assets/videos/demo.mp4"
numberは一意(重複してはいけない /numberを変えずにschedule_atやtextだけ変えて再利用するような形での履歴上の重複も基本的には NG ですので注意してください)、かつ必須です。schedule_atは ISO 形式。タイムゾーンが無い場合はtimezoneを適用します。threadはネストで表現します。- 子要素の
schedule_atは省略可能(親の値を継承)です。 - 子要素に
schedule_atを書く場合は親より前の時刻を指定することはできません。
- 子要素の
imagesは最大4つ(pathとaltの組み合わせが4セット)記述可能。videoとは同時指定不可です(画像や動画のパスはtweets.yamlからの相対パスで記述してください)。altは各画像ごとに設定可能です。省略した場合、画像の ALT は未設定として投稿されます。- 1つのポストごとに、
textまたはimagesorvideoのいずれかが必須です(textはimagesorvideoのいずれかと併用可能)。
thread配列は「親への返信(子ツイート)」として投稿されます。- 兄弟(同じ親の直下)同士は独立です。順番に投稿したい場合は「子の中にさらに子」としてネストしてください。
schedule_atを省略した子は、直近の親の日時を継承します。
logs/tweet-log.jsonlに JSONL 形式で出力します。- 投稿済み判定は
state/post-history.jsonに記録されます。
GitHub Actions から logs/tweet-log.jsonl と state/post-history.json を push するため、リポジトリ設定の Actions > Workflow permissions を "Read and write" に設定してください。(ブランチ保護がある場合は Actions の push を許可してください)