丟一個主題,系統自動生出 IG 輪播圖、寫好文案,一鍵排程發到 IG、Threads、Facebook,發完自動記帳、通知手機。原本一篇手動做一兩小時,整條串起來幾分鐘跑完,還能排定時自動發。
照順序裝,每步附指令跟驗證方法。裝到哪、驗到哪,一張圖能手動發成功再往下,不要跳步。
版本 2026-07-06 · 帳號 ID/金鑰皆為<佔位符>,填你自己的
| 平台 | 用途 | 要開什麼 | 申請 App 要勾的權限 |
|---|---|---|---|
| 主發佈(輪播) | IG 商業帳號+綁 FB 粉專+Meta 開發者 App | instagram_basic、instagram_content_publish、manage_comments、manage_messages | |
| Threads | 純文字擴散 | Threads 帳號+Threads API App | threads_basic、threads_content_publish |
| Facebook 粉專 | 圖文,唯一支援原生排程 | FB 粉專+企業商家型 App | pages_manage_posts |
| Cloudflare R2 或 Pages | 圖床(發文前置,必要) | Cloudflare 帳號 | Public 存取 |
| Notion | 內容日曆/名單台賬 | Notion workspace+Integration | Integration 要在該頁 Connections 分享 |
| Telegram | 發佈前審查+通知 | BotFather 建一支 Bot | — |
| LINE | 通知 | LINE 官方帳號+Messaging API | — |
| 生圖服務(如 Higgsfield) | AI 生圖 | 生圖帳號+API/CLI | — |
| 工具 | 用途 | 安裝 |
|---|---|---|
| Python 3 | 全產線 | 系統內建/brew |
| Playwright + Chromium | HTML 截 PNG | pip install playwright && playwright install chromium |
| requests | 打 Graph API | pip install requests |
| PyYAML | 讀寫主題包 | pip install pyyaml |
| rclone | 上傳圖床 | brew install rclone |
| boto3(選) | 程式上傳 R2 | pip install boto3 |
| launchd(mac)/cron(Linux) | 排程 | 系統內建 |
| sips(mac) | 驗裁切、壓圖 | 內建 |
每支 .env 設 chmod 600,加進 .gitignore,不上版控。
| 服務 | 檔名 | 變數 | 去哪拿 |
|---|---|---|---|
instagram.env | IG_USER_ID、IG_LONG_TOKEN | Meta 開發者後台,短 token 換 60 天長效 | |
| Threads | threads.env | THREADS_USER_ID、THREADS_TOKEN、THREADS_APP_ID、THREADS_APP_SECRET | Threads API 後台,測試人員手機接受邀請後產 token |
fb.env | FB_APP_ID、FB_APP_SECRET、FB_PAGE_ID、FB_PAGE_TOKEN | 企業商家型 App,換永久 Page Token | |
| Cloudflare R2 | r2.env | R2_ACCOUNT_ID、R2_ACCESS_KEY_ID、R2_SECRET_ACCESS_KEY、R2_BUCKET、R2_PUBLIC_BASE | R2 → Manage API Tokens 建 S3 金鑰 |
| Notion | .env | NOTION_TOKEN+各 DB id | Notion → My integrations |
| Telegram | telegram.env | TELEGRAM_BOT_TOKEN、TELEGRAM_CHAT_ID | BotFather |
| LINE | line.env | LINE_CHANNEL_ACCESS_TOKEN、LINE_USER_ID | LINE Developers Messaging API |
IG/Threads 長效 token 約 60 天,排程每週刷。FB Page Token 可設永久(expires_at=0)。
IG_USER_ID 跟短 token。instagram.env。# 建容器
curl -X POST "https://graph.instagram.com/<IG_USER_ID>/media" \
-d "image_url=<公開圖URL>" -d "caption=測試" -d "access_token=<IG_LONG_TOKEN>"
# 拿回 {"id":"<creation_id>"},等 5 秒發佈
curl -X POST "https://graph.instagram.com/<IG_USER_ID>/media_publish" \
-d "creation_id=<creation_id>" -d "access_token=<IG_LONG_TOKEN>"
media_id,IG App 上看到那則貼文。這步不過,後面全部免談。IG Graph API 只吃公開 https 圖,本機檔一律拒收。發文前所有圖先上圖床拿公開網址。
用 Cloudflare R2:
rclone config create r2 s3 provider=Cloudflare \
access_key_id=<R2_ACCESS_KEY_ID> secret_access_key=<R2_SECRET_ACCESS_KEY> \
endpoint=https://<R2_ACCOUNT_ID>.r2.cloudflarestorage.com
rclone copyto 本地.png r2:<R2_BUCKET>/ig/2026-07-06/檔名.png
# 公開網址 = <R2_PUBLIC_BASE>/ig/2026-07-06/檔名.png
或用 Cloudflare Pages 靜態站當免費圖床,把 PNG 推上去拿 xxx.pages.dev/...。
驗證每張上傳的圖:
curl -w "HTTP %{http_code} %{content_type} %{size_download}\n" -o /dev/null -s <URL>
HTTP 200、content-type 對、bytes 跟原檔一樣,三項全過才算。403/404 是 bucket 沒設公開存取。
生圖服務本來就回公開 URL(如 Higgsfield)的圖,直接用,不必再上傳。
封面、內頁、結果頁三個 4:5 樣板,套下面規則,先手填文字截出一張合格圖。
尺寸(HTML 上的 frame-XxY class 決定):
| class | 尺寸 | 用途 |
|---|---|---|
frame-4x5 | 1080×1350 | 輪播三頁都用這個 |
frame-9x16 | 1080×1920 | Reel 片頭/Stories |
frame-1x1 | 1080×1080 | 方形貼文 |
安全區規則(不照做會被 IG 介面切掉頭跟字):
| # | 檢查 | 門檻 |
|---|---|---|
| 1 | 底部操作條=底 22% | 重要文字:4:5 落在 y≤1053;9:16 落在 y≤1498 |
| 2 | 格狀縮圖裁切(1:1 置中,上下各裁 135px) | 頭/文字/handle 全在 y135~1215 |
| 3 | 標題字級 | ≥ 32px |
| 4 | 內文字級 | ≥ 16px |
| 5 | 對比 WCAG AA | 一般字 ≥4.5:1、大字 ≥3:1 |
| 6 | Emoji | 每張 ≤3,封面 0~1 |
全身模特照 full-bleed 進 4:5 防破框,照片下壓 155px:
.s.photo .photo, .s.photo .ph_img { top: 155px; } /* 頭最高只到 y155 */
.s.photo { background: #13160F; } /* 頂部深色帶 */
色票(範例,換你自家):
| 用途 | 色碼 |
|---|---|
| 底色(禁純白) | #FAF6EE |
| 主文字 | #1F2823 |
| 輔色 | #9CB5A6 |
| CTA | #E85D3C |
| 安全區深頂帶 | #13160F |
配色 60% 底+主色 : 30% 功能色 : 10% CTA。字型:標題襯線、內文思源黑 300、標籤 Tenor Sans。
驗證裁切:sips -c 1080 1080 封面.png --out 檢查.jpg,目視頭跟字有沒有被切。
Playwright 開無頭 chromium,把文字跟照片注入底版 selector,截 PNG。
python3 出圖.py --html 底版.html --out 輸出.png \
--photo 照片.png \ # 注入 .photo img
--text '.hook=封面鉤子文字' \ # 注入 selector innerHTML,可多次
--hide-safe-zone # 截圖前移除輔助虛線
截圖用 device_scale_factor=1,只擷取 frame 區塊。
輪播三步,host 是 graph.instagram.com:
1 每張子圖 POST /<IG_USER_ID>/media
image_url=<公開URL> is_carousel_item=true → 各拿 child creation_id
2 建父容器 POST /<IG_USER_ID>/media
media_type=CAROUSEL children=<id,id,...> caption=<文案> → parent id
3 等 5 秒 POST /<IG_USER_ID>/media_publish
creation_id=<parent> → 回 media_id
驗證 GET /<media_id>?fields=permalink,media_type,timestamp → 撈 permalink
發完一定要撈到 permalink 才算成功,送出不等於發成功。
Reel 帶 media_type=REELS+video_url,限動帶 media_type=STORIES,影片要輪詢容器 status_code=FINISHED 才能發。
一支主腳本把「主題 → 生圖 → PNG → 安全區 → 上 CDN → 發佈」串起來,任一步失敗停下來回報,不硬往下衝。
主題包 YAML(鍵名層級一字不差,carousel 底下只能是 封面/內頁/結果頁,多內頁用 內頁1…內頁4):
期數: "003"
日期: "2026-06-28"
主題: "<一句話主題>"
hashtag: "#A #B #你的品牌tag"
carousel:
封面:
photo_prompt: >
<英文場景,no people no faces no text no logos>
文字:
".hook": "<封面鉤子>"
內頁:
photo_prompt: >
<英文場景>
文字:
".hero-num": "<主數據>"
".body-zh": "<內文 ≤60字>"
結果頁:
photo_prompt: >
<英文場景>
文字:
".hl-block": "<結果重點>"
".cta-italic": "<CTA:留言『X』我私訊你>"
caption: |
<IG 發文全文:鉤子→內容→單一CTA→hashtag 8~15個>
字數上限(4:5 安全區小,寧少勿多):封面鉤子 ≤20 字、內頁大字 ≤10 字+內文 ≤60 字、結果頁結論 ≤16 字+CTA ≤24 字。photo_prompt 一律英文,結尾 no people no faces no text no logos。
組完跑 PyYAML 語法驗:頂層鍵齊、carousel 鍵集合等於 {封面,內頁,結果頁}、每頁有 photo_prompt。
排程用 launchd(mac,plist 放 ~/Library/LaunchAgents/),指向非保護目錄,別指 ~/Desktop(新版 macOS 擋 launchd 讀桌面)。Linux 用 cron。
| Job | 頻率 | 動作 |
|---|---|---|
| 留言回覆+私訊 CTA | 每 2 分鐘輪詢 | 掃留言→公開回覆+私訊 CTA,handled.json 去重 |
| Token 刷新 | 每週日 | 刷 IG/Threads 長效 token |
| 排程佇列 | 定時 | 掃 Threads 本地佇列到點發 |
發佈前 TG 審查(多平台扇出時必備),把預覽推到 Telegram,附三顆 inline 按鈕:核准全發/退改/跳過。人點核准才發:
send <批次ID> <平台清單> → 推審查訊息+三顆按鈕
poll <批次ID> <秒數> → 讀回 {"action":"approve|revise|skip","platforms":[...]}
規則:讀不到 approve 一律不發,逾時全停待發不自動發。圖/影片本機檔先上 CDN 拿可預覽 URL 再推。送出的 bot 要跟輪詢的 bot 同一支,否則互搶訊息。
回填 Notion:發完把 發文ID、完成素材URL、狀態(已發布)寫回內容日曆,寫完重新讀一次逐欄比對才算生效。失敗把「狀態=失敗」+「失敗原因」一起寫。
推播:一支 notify.py 同時推 Telegram+LINE,單邊掛不擋另一邊。
IG 通了再複製到其他平台。
Threads 兩步,host graph.threads.net/v1.0:
1 POST /<THREADS_USER_ID>/threads
media_type=TEXT text=<≤500字> (帶圖:media_type=IMAGE image_url=<URL>) → creation_id
2 等 5 秒(帶圖 30 秒)
POST /<THREADS_USER_ID>/threads_publish creation_id=<id> → media_id
驗證 GET /<media_id>?fields=permalink,timestamp
Threads 無原生排程,走本地佇列+launchd 到點才發。API 不能刪文,只能 App 手動刪。
FB 發文,host graph.facebook.com/v21.0:
純文字 POST /<FB_PAGE_ID>/feed message=<文案>
單圖 POST /<FB_PAGE_ID>/photos url=<公開圖URL> caption=<文案>
多圖 每張 POST /photos url=<URL> published=false → 各拿 media_fbid
再 POST /feed message=<文案> attached_media[0]={"media_fbid":"<id>"}
原生排程 發佈加 published=false scheduled_publish_time=<unix秒> (須 ≥現在+10分、≤+6個月)
驗證 GET /<post_id>?fields=permalink_url,is_published,scheduled_publish_time
FB 是三平台唯一支援 API 原生排程的,多平台定時發就靠它。
扇出(選配):一份主題,為四平台各挑錯開角度(IG 教學拆解、FB 長文+主圖、Threads 短觀點、數字人口播),並行生成→TG 審查→核准後排程發。四平台角度不能重複。
| 會卡在哪 | 怎麼過 |
|---|---|
| CTA 連結還是佔位符 | 上線前把 config 的 cta_link 改成真報名連結 |
| IG 不吃本機圖 | 發文前必上 CDN,對照表存 manifest.json |
| macOS 擋 launchd 讀桌面 | 運作檔複製到 ~/.隱藏目錄/,launchd 指那裡 |
| FB 加不進發文權限 | App 選企業商家型,不是廣告型 |
| IG 不能刪文 | 只能瀏覽器手動刪 |
| TG 審查 bot 互搶訊息 | 送出跟輪詢用同一支 bot |
| Token 過期 | 排程每週刷 IG/Threads,FB 設永久 |
| 喊這句 | 啟動 |
|---|---|
| 一鍵發IG/IG一條龍/一鍵生整篇 | 生圖+發佈全鏈 |
| fansi 試跑(換你的詞) | 只做到 PNG+caption,不真發 |
| 把這份輿情扇出/內容工廠 | 多平台扇出 |
| 回留言/設定 IG 自動回覆 | 留言客服+名單 |