社交媒体定时发布
目的
从一个地方按计划发布启动内容,通过每个平台的官方 API,避免将个人账户丢失给品牌,或将品牌账户丢失给个人。本教程使用的工具是 Postiz(开源、自托管);平台相关步骤适用于任何调度器。
先决条件
- 品牌账户已创建:Instagram 专业账户、Threads 个人主页(来自 Instagram)、X 账户,可选 TikTok。
- 在 Cloudflare 上有一个域名(用于隧道主机名)、Docker Desktop、一台在发布时段保持开机的机器(或小型 VPS)。
- 如果从浏览器驱动控制台,需要
console-automation设置。 - 内容日历(见下文“日历”部分)。
平台现状(开始前再次检查,因为会变化)
| 平台 | 官方途径 | 阻碍因素 |
|---|---|---|
| Meta 应用 + Facebook 登录,链接到 Facebook 主页的 IG 专业账户 | 新的 Meta 应用会获得“统一登录”的 Instagram 设置,这需要一个业务组合;旧的仅 Instagram 登录设置“对新应用不可用”。快拍以纯媒体形式发布:无法通过 API 发布带有投票、链接或倒计时贴纸的快拍。 | |
| Threads | 带有 Threads 用例的 Meta 应用 | 在应用审核通过之前,只有拥有 Threads 测试者角色的账户才能连接;测试者在 Threads › 设置 › 账户 › 网站权限 › 邀请中接受邀请。 |
| X | 具有付费 API 访问权限的开发者账户(查看当前 X API 定价) | 需要支付方式;设置消费上限。 |
| TikTok | 内容发布 API | 未经审核的应用只发布私密内容,直到审核通过(审核需要时间)。新账户需要电话验证,一个号码只能属于一个账户。 |
| YouTube | Data API 上传 | 未经验证的 API 项目上传为私密,直到通过合规性审核。 |
围绕阻碍因素制定计划:尽早开始审核,在审核通过前原生发布 TikTok/YouTube(TikTok Studio、YouTube Studio 调度器),或放弃该平台。
操作步骤
1. 自托管 Postiz(Docker)
- 从官方的
postiz-docker-compose文件开始。在执行up之前:- 移除你不需要的服务,以及任何占用机器上已有端口的服务(上游文件将 Temporal UI 绑定到
127.0.0.1:8080)。 - 删除 Elasticsearch:设置 Temporal
ENABLE_ES=false和SKIP_ADD_CUSTOM_SEARCH_ATTRIBUTES=true(SQL 可见性)。Postiz 只注册两个关键字属性。如果 Temporal 首次启动时启用了 ES,其数据库仍会保留默认的文本属性,Postiz 会因“不能有超过 3 个类型为 Text 的搜索属性”而失败:在调度任何任务之前,重新创建 Temporal 数据库卷。 - 仅将 Postiz 绑定到
127.0.0.1;将MAIN_URL、FRONTEND_URL、NEXT_PUBLIC_BACKEND_URL设置为公共 HTTPS 主机名。 - 将密钥(
JWT_SECRET、隧道令牌、应用密钥)放入 git 忽略的.env文件中,在 compose 文件中使用${VAR}引用。 - 为每个服务添加
mem_limit/cpus。Postiz 在一个容器中运行前端、后端和编排器,我们观察到大约 1.5 GB 的使用量,因此 2 GB 的限制可以避免达到上限。在 Windows 上,还要在%USERPROFILE%\.wslconfig中限制 WSL VM(memory=4GB、processors=4、autoMemoryReclaim=gradual),然后运行wsl --shutdown并重启 Docker Desktop。
- 移除你不需要的服务,以及任何占用机器上已有端口的服务(上游文件将 Temporal UI 绑定到
- 在浏览器中页面水合之后创建第一个账户(未水合的表单会以 GET 方式提交,并将密码放在 URL 中)。然后设置
DISABLE_REGISTRATION=true并重启。 - 保留
restart: always并启用“登录时启动 Docker Desktop”;如果机器是调度器,请禁用睡眠。
2. 公共主机名(Cloudflare Tunnel)
- 创建一个远程管理的隧道(API:使用
config_src: cloudflare调用POST /accounts/{id}/cfd_tunnel,调用PUT .../configurations设置入口规则,创建一个代理的CNAME指向<tunnel-id>.cfargotunnel.com,调用GET .../token)。 - 将
cloudflared作为服务运行在相同的 compose 网络中,并将入口指向http://postiz:5000,而不是localhost。 - 审批:隧道会将本地服务暴露到互联网;创建前需获得批准。可选地在前面添加 Cloudflare Access(需要账户启用 Zero Trust;保持
/uploads可访问,以便 Meta 获取媒体)。
3. Meta(Instagram + Threads)
- 为发布创建一个单独的 Meta 应用(不要向已为应用内功能提供支持的应用添加用例,例如 Instagram 快拍分享)。用例如下:“管理 Instagram 上的消息和内容”和“访问 Threads API”。
- Instagram:
- 当用例要求时创建一个业务组合(免费;名称、所有者姓名、业务邮箱)。只有在需要他人数据和应用审核时才需要验证。
- 为品牌创建一个Facebook 主页(Meta 广告也需要),并将 Instagram 专业账户链接到该主页:主页设置 › 关联账户 › Instagram › 连接。OAuth 弹窗将使用浏览器当前持有的 Instagram 账户登录:阅读“以……身份登录”的名称;如果是个人账户,请先切换账户(连接个人账户会将其转换为专业账户)。
- 在 Instagram 用例中,精确添加调度器请求的作用域(Postiz:
instagram_basic、pages_show_list、pages_read_engagement、business_management、instagram_content_publish、instagram_manage_comments、instagram_manage_insights)。将用于企业的 Facebook 登录的重定向 URI 设置为调度器的回调地址(Postiz:/integrations/social/instagram)。 - 应用 ID + 密钥(应用设置 › 基本资料;显示密钥需要输入 Facebook 密码)→
FACEBOOK_APP_ID/SECRET。
- Threads:添加
threads_basic、threads_content_publish、threads_manage_replies、threads_manage_insights;设置重定向(/integrations/social/threads)以及卸载/删除回调 URL;获取单独的 Threads 应用 ID/密钥;将品牌主页添加为 Threads 测试者,并在手机上接受邀请。 - 如果 Threads 同意 URL 最终跳转到 Threads 主页而非同意屏幕,则说明浏览器会话位于
threads.com,而调度器使用的是threads.net:在threads.com上打开相同的 URL。 - 如果浏览器当前以主页身份运行(在编辑主页后),Facebook 同意首先显示“你需要切换到<你的名字>”;以个人身份继续,而不是以主页身份。
- 在 Facebook 同意过程中,Meta 默认选择“所有当前和未来的主页/业务/Instagram 账户”。在管理其他品牌的账户上,选择“仅当前”并选择品牌的资产。
4. X
- 开发者控制台入门:账户名称、精确的用例描述(仅自己的账户,不转售数据,不自动点赞/关注/DM),接受条款。
- 购买最小的积分包;关闭自动充值并设置计费周期上限。仪表板总数可能滞后于积分页面。
- 应用 › 用户身份验证设置:阅读和写入,“Web 应用、自动应用或机器人”,回调 = 调度器回调(Postiz:
/integrations/social/x)、网站、服务条款和隐私 URL。保存后会显示一次 OAuth 2.0 客户端密钥。 - Postiz 使用 OAuth 1.0a:重新生成消费者密钥并复制密钥 + “消费者密钥密文”(确切标签)一次 →
X_API_KEY/X_API_SECRET。 - 授权:勾选“我信任此应用”,确认屏幕显示品牌句柄,然后授权。
5. TikTok(可选)
账户:
- 新账户需要电话验证,一个号码只能用于一个账户;通过网页注册可能会以“在应用中继续”结束。选项:为品牌账户使用第二个号码,或转换现有的个人账户(新的用户名和显示名称、个人资料照片、简介;归档/隐藏旧的个人视频)。
- 在某些地区,“切换到企业账户”的一键选项已消失;菜单中提供“成为已验证的企业账户”,这需要公司文件。个人账户就足够了:TikTok Studio 调度、分析、Spark Ads 和内容发布 API 都适用。企业音乐库是主要损失。
- 用户名每 30 天可更改一次,显示名称每 7 天可更改一次。网页个人资料(名称、简介、照片、计数)可能比应用延迟一天或更久;以应用为准。
- 账户地区在注册时由 SIM/IP 决定,且无法在设置中更改。
开发者应用(用于调度器):
- developers.tiktok.com 注册只显示“企业开发者”(邮箱 + 验证码 + 密码),但在创建应用时,你可以选择个人所有权,因此开始不需要公司。
- 创建应用,然后创建一个沙箱:基本信息(图标 1024²、类别、描述 ≤120 字符、服务条款/隐私 URL、Web 平台 URL)、产品 Login Kit(Web 重定向 URI = 调度器回调;Postiz
/integrations/social/tiktok)和 Content Posting API(开启直接发布),以及调度器请求的所有作用域。Postiz 请求user.info.basic、user.info.profile、user.info.stats、video.list、video.upload、video.publish;缺少任何一个都会在同意页面上显示“出了问题……更正以下内容:作用域”。每次编辑后点击“应用更改”。 - 将品牌账户作为沙箱目标用户添加(登录弹窗接受手机应用的二维码扫描)。在调度器中使用沙箱客户端密钥/密文。
- 通过文件推送不需要域名验证;通过 URL 拉取则需要。
- 审核前:
UPLOAD模式将视频发送到账户的 TikTok 收件箱作为草稿(24 小时内从手机发布;每 24 小时最多 5 个待处理)。DIRECT_POST模式有效,但仅在私密状态下发布。提交生产应用供审核,并附上用于公开直接发布的演示视频。 - 同意步骤可能要求通过邮箱验证码“验证是你本人”。在一天内进行大量账户更改(邮箱、名称、简介、新登录)后,TikTok 会限制这些验证码(“已达到最大尝试次数”):停止操作,第二天再试,不要继续尝试。
6. 验证
- 调度器频道列表显示每个品牌账户(名称/句柄),没有被禁用的。
- 在日历开始前,在每个平台上发布一个私密/低风险的测试内容,或一个草稿。
- 一天后的资源使用情况:
docker stats --no-stream。
日历
- 将发布日历与真实的发布事件挂钩,而不是固定日期:发布前(无下载行动号召,无价格),然后每个商店的发布,最后两者上线。商店审核日期会变动;当它们变动时,重新调整日历。
- 对照已发布的版本检查帖子中的每个声明,并对照每个商店实际在线的内容检查每个行动号召。
- 带有投票、链接或倒计时的快拍无法通过 API 发布;请通过应用发布。
需要人工批准的决策点
创建品牌账户、业务组合、Facebook 主页;通过隧道暴露本地服务;购买 API 积分和设置消费上限;哪些个人账户永远不能连接;转换个人账户;删除 Docker 卷或数据。
常见陷阱
- 因为 OAuth 弹窗复用了浏览器的会话,导致连接了个人 Instagram 账户。
- 假设在全新的 Meta 应用上存在旧的 Instagram 登录设置。
- 将发布用例添加到移动应用内部使用的 Meta 应用中。
- 当 Temporal SQL 可见性足够时,仍保留 Elasticsearch 在栈中。
- 上游的 compose 文件占用了本地其他服务已使用的端口。
- 从错误的标签处保存了 X 密钥(“消费者密钥密文”,而不是对话框标题)。
- 日历编写成仿佛应用在第一天就在两个商店上线。
- 在同一天完成所有 TikTok 账户更改和开发者连接,这会触发验证码速率限制。