soia-team/soia-open-media-content-skills · Archived

soia-media-publish-x-article

将 Markdown 成文上传到 X Articles 草稿箱并校验格式,只保存草稿。触发:「发成 X Article」「推到 X 文章草稿箱」

First seen Jul 25, 2026

Installation

$ npx skills add soia-team/soia-open-media-content-skills --skill soia-media-publish-x-article

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from soia-team/soia-open-media-content-skills.

npx skills add soia-team/soia-open-media-content-skills

Browse all from soia-team/soia-open-media-content-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Not declared
Codex Declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Declared
Cline Not declared
OpenCode Declared

Repository health

Stars 3
License licenses
Default branch main
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

Version2.0.2
Declared agents claude-code codex gemini opencode

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 19,344 B
  • docs SUMMARY.md 182 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 3 installs

SKILL.md

soia-media-publish-x-article

把 Obsidian/本地 Markdown 成文草稿直传 X Articles 草稿箱,补齐 publish 家族的 X 长文出口(publish-x-thread 产推文串文本,本技能产富文本长文草稿)。

客户可读说明

这个技能可以做什么

客户想要 技能会做 客户能看到
把一篇 Markdown 长文发到 X Articles 解析标题/封面/正文图 → 浏览器里新建草稿 → 富文本粘贴 → 按原位插图 → 机械校验 浏览器实时操作过程、草稿 URL、校验清单和最终回执
文章没有封面图 停下来提醒(X Article 无封面观感差),明确同意后才无封面继续 提醒与确认问题
缺依赖/未登录/无 Premium+ 停止并明确指出缺什么 登录/开通指引,不代填任何凭据

安全底线:只保存草稿,绝不点击「发布/Publish」;登录态只留在浏览器里,不导出 cookie、不写任何凭据到磁盘。

客户如何使用

其他可识别说法包括「上传到 X 文章」「X Articles draft」「把这篇发 X 长文」;只有普通短帖或 thread 时转交 soia-media-publish-x-thread。

  1. 说「把 <文件> 发成 X Article」。首次使用需要登录一次:脚本弹出浏览器窗口,人工登录后长期复用(与 soia-media-publish-x-thread 共用同一份登录态,任一技能登录过就都不用再登);账号需订阅含「撰写文章」权益。
  2. Agent 先 dry-run 解析并汇报:标题、封面、正文图数量、缺图清单;封面缺失时先问你。
  3. 浏览器阶段全程可见(默认走宿主无关的 Playwright 脚本,任何能跑 Python 的环境都一样);完成后给草稿 URL 和校验清单,由你人工审阅并发布。

依赖与安装

claude plugin marketplace add soia-team/soia-open-skills
claude plugin install soia-media-content@soia

只要这一个技能时,可用 npx 路线。注意技能会落进共享真源 ~/.agents/skills;若同时装了插件,同一技能会出现两份索引且各自漂移,建议二选一:

npx skills add soia-team/soia-open-media-content-skills -g -a '*' -s soia-media-publish-x-article -y
  • 跨平台 Python 3.9+(macOS / Windows / Linux 均可):主路线用 Playwright 自带浏览器 + 原生剪贴板 API,不依赖 osascript / sips 等系统专属工具。

``bash pip install playwright && python -m playwright install chromium ``

  • 登录态与 soia-media-publish-x-thread 共用同一份本地 profile 目录(~/.config/soia-skills/soia-media-publish-x-thread/x-profile,可用 --profile-dir / SOIAXPROFILEDIR 覆盖;脚本会对旧命名空间做只读回退):已经用 xpost.py login 登录过就直接可用;未登录时去该技能跑一次 python3 scripts/x_post.py login(本技能自身不提供 login 子命令,靠共用 profile 免二次登录)。
  • 备选路线(宿主恰好提供浏览器控制工具,如 Claude Code 的 claude-in-chrome)需要 macOS,走 clipboard_x.py(osascript/sips);详见下方「备选路线」。没有宿主工具、或在非 macOS 环境,用主路线即可。
  • X 账号需含「撰写文章」权益的 Premium 订阅(2026-07 购买页显示 US$4 的 Premium 档即含;以 X 购买页实时权益表为准)
  • 无私有 config.yml:本技能不接触任何 key/cookie

WorkBuddy 的装载单位是角色化专家而不是插件,npx skills add -a '*' 覆盖不到它,需要单独安装,见 docs/install/workbuddy.md。

日志与完成回执

执行完必须输出:做了什么、草稿 URL、校验结果(逐项)、跳过/失败项、下一步(人工审阅发布)。

定位与闭环

compose-article-draft 产成文 → 本技能推 X Articles 草稿(对标 publish-wechat-draft 之于公众号)。「只建草稿绝不发布」与公众号侧同一条安全哲学。

触发词

用户说 动作
「发成 X Article」「上传 X 文章」「推到 X 草稿箱」 完整流程(解析 → 上传 → 校验)
「先看看这篇能不能发 X」 只跑解析 dry-run,汇报不动浏览器
「发条 X/发个推」(普通短帖) 不归本技能:短帖/thread 文本用 soia-media-publish-x-thread;本技能只做 Articles 长文草稿

工作流

第 1 步:解析(不动浏览器)

python3 scripts/parse_x_article.py <article.md> > /tmp/x_article.json
python3 scripts/parse_x_article.py <article.md> --html-only > /tmp/x_article_body.html

JSON 字段:title / coverimage(仅当文章以图片开头)/ contentimages[](含 blockindex 与 aftertext)/ dividers[] / html / missing_images。

两道闸门,先过再动浏览器:

  1. missing_images > 0 → 停,把缺图清单给用户(可用 --search-dir 指定补找目录)。
  2. cover_image 为 null → 停,提醒「X Article 无封面观感差,建议在文首加一张图」;用户明确说不加才继续(此时全部图片按正文图处理)。

第 2 步:草稿写入 X Articles

主路线是自带的 Playwright 脚本 xarticlepost.py,宿主无关——在 Claude Code、Codex、Gemini CLI、opencode 等任何能跑 Python 的宿主中行为一致,不依赖任何宿主专属浏览器工具;两条路线共享同一套第 3 步机械校验标准。

主路线:scripts/xarticlepost.py(宿主无关)

依赖(一次性):pip install playwright && python -m playwright install chromium。

python3 scripts/x_article_post.py status                     # 检查登录态(与 x_post.py 共用 profile)
python3 scripts/x_article_post.py check                      # 探测 Articles 入口
python3 scripts/x_article_post.py draft /tmp/x_article.json  # 完整草稿:封面→标题→正文→插图→分割线→校验
  • 登录:本技能自身不提供 login 子命令。未登录时去 soia-media-publish-x-thread 跑一次 python3 scripts/x_post.py login,两边共用同一份本地 profile,登录一次两边都能用。
  • check 返回四种状态之一:

- notloggedin → 未登录,按上面指引登录后重试。 - noarticlesaccess → 账号未开通「撰写文章」权益,或 /compose/articles 404;JSON 里带下面这条固定提醒文案。 - ok → 找到可点击的创建入口,可以继续 draft。 - unknown → 页面结构与预期不符(可能编辑器改版、也可能是从未验证过的真实 DOM),JSON 附截图路径,不猜测点击;按下方「边界与异常」处理。

noarticlesaccess 固定提醒文案(绝不代为订阅;与脚本 NOPREMIUMMESSAGE 常量逐字节一致,非改述): > 当前账号未开通含「撰写文章」权益的 X Premium,请到 https://x.com/i/premiumsignup 开通(2026-07 实测 US$4/月的 Premium 档即含此权益)后再试。

  • draft 内部第一步就是复用 check 的判定:不是 ok 立即停并原样返回该状态,不会继续点任何东西。
  • 正文图与分割线一律按 block_index 从大到小倒序插入——编辑器是动态文档,先插前面的会移动后面所有锚点,这是硬性正确性要求,不是风格偏好。
  • 剪贴板走 Playwright 原生 Clipboard API(navigator.clipboard.write + 授予的 clipboard 权限),macOS / Windows / Linux 通用;粘贴键按平台自动选择 Cmd+V(macOS)/ Ctrl+V(其它平台),不依赖 osascript。
  • 封面上传走真实文件输入控件(input[type=file] + setinputfiles),比剪贴板/拖拽更稳。
  • 没有任何发布相关代码路径:xarticlepost.py 里不存在能点击或定位「发布」按钮的代码——这是脚本本身的硬约束,比 publish-x-thread 的 draft/send 两档更严格(Articles 场景下 send 这一档根本不存在)。
  • 未预期的异常会被整体兜底:产出结构化错误 JSON(错误信息 + 页面 URL/标题 + 截图路径),不会抛裸 traceback,也不会带着异常状态继续瞎点。
  • 输出为单行 JSON 回执,任何宿主可机读。

备选路线:宿主浏览器工具

宿主恰好提供浏览器控制(如 Claude Code 的 claude-in-chrome,经浏览器扩展复用用户真实 Chrome 登录态)时可选用,客户零登录成本,页面改版时人工介入也更直观;没有宿主工具时不得以此为由跳过脚本主路线。校验标准与主路线完全一致(见第 3 步)。

  1. 浏览器面选择:claude-in-chrome(真实 Chrome 已登录)或 Browser pane(未登录则让用户登录,不代输凭据)。
  2. 导航 https://x.com/compose/articles——落地是草稿列表页,不是编辑器;直接点「create/撰写」按钮进编辑器(不要等「添加标题」出现,它在点击后才渲染)。
  3. 跳到登录页 = 未登录;/compose/articles 404 或创作者工作室无「文章」入口 = 账号无 Articles 权益。停下并按固定话术提醒(绝不代为订阅,文案同上)。
  4. 有封面:点封面上传控件,用文件上传把 cover_image 传上去;X 会弹媒体编辑层,必须点「应用/Apply」关掉,否则编辑器被遮罩挡住、封面不生效。标题框(placeholder「添加标题/Add title」)输入 title。
  5. 正文富文本粘贴:

``bash python3 scripts/clipboardx.py html --file /tmp/xarticle_body.html ` 点编辑器正文区 → Cmd+V`。标题/加粗/链接/列表/引用由 HTML 剪贴板 flavor 保留。粘贴后抽查首段和末段文字是否都在。

  1. 插图与分割线(都按 blockindex 从大到小倒序):对每张 contentimages[i]:

``bash python3 scripts/clipboardx.py image <path> --max-bytes 3000000 ` 用 aftertext 找到目标段落 → 点击 → 按 End 键把光标推到段落尾(防止点进段内链接)→ Cmd+V 粘贴 → 等「正在上传媒体/Uploading」消失再插下一张。分割线(dividers[],同样倒序):X 忽略 <hr>`,必须点对应位置后用编辑器「插入 > 分割线」菜单。

> 倒序原因:编辑器是动态文档,先插前面的会移动后面所有锚点。

第 3 步:机械校验(不过全部就不算完成)

校验项 标准
标题 编辑器标题 == JSON title
正文首/末句 都能在编辑器中找到
媒体数 编辑器内图片数 == 封面(0/1) + content_images 数
分割线数 == dividers 数
草稿状态 autosaveconfirmed:插图/分割线全部处理完后,先等一段时间给(假定存在的)防抖自动保存生效,再尝试观测「已保存/Saved」提示;drafturl:记录草稿 URL

主路线的 draft 子命令在 JSON 回执的 checks 字段里已经把这五项跑完(drafturl/autosaveconfirmed 拿不到确切结果时诚实给 null/false,不编造;autosave_confirmed: false 不等于失败,只是没能确认,按上方「边界与异常」处理);备选路线需要人工或宿主浏览器工具逐项核对。

第 4 步:回执

草稿 URL + 校验清单逐项结果 + 「请人工审阅后自行点击发布」。任何情况下不点发布。

格式支持边界

元素 处理
H2/H3、加粗、斜体、链接、有序/无序列表、引用 原生保留
代码块 转引用块(X 不支持 <pre>)
表格 / mermaid X 不支持:先手工转成 PNG 图插入,或接受降级丢失;解析器原样透传文字
--- 分割线 编辑器 Insert/插入 菜单插入(主路线见 xarticlepost.py 的 insertdividerat,备选路线见第 2 步步骤 6)
H1 只作标题,不进正文

边界与异常

场景 处理
跳登录页 停,让用户在浏览器登录后重试;不碰凭据
无 Articles 入口(/compose/articles 404 或无创建入口) 停,按第 2 步固定话术提醒并给开通直链 <https://x.com/i/premiumsignup>;绝不代为订阅;主路线对应 check/draft 的 noarticlesaccess 状态
页面结构无法确定属于以上哪种情况 停,如实报告为 unknown(主路线自动附截图 + 页面 URL/标题),不强行归类、不猜测点击
缺图 第 1 步闸门拦截,给清单
无封面 闸门提醒,明确同意才继续
粘贴后格式丢失 检查剪贴板路径(主路线看 JSON 里的 warnings;备选路线检查剪贴板脚本输出);重试一次;仍失败则报告实际效果,不硬说成功
图片上传卡住 等待上传指示消失(主路线有 15s 超时上限);超时则截图报告,不盲目继续
分割线插入失败 不中止整份草稿:主路线把失败项计入 warnings(含 blockindex/aftertext),其余内容照常完成
X 编辑器改版找不到控件 停下报告页面实况,不猜测点击
draft 中途失败后重试 主路线每次调用都会重新点「create」新建一篇文章,不会续写/去重上一次失败的草稿;重试前若怀疑上次已部分写入,先去 X 的 Articles 草稿列表人工检查并清理重复/半成品草稿,重试只会新增一篇,不会自动合并或覆盖
checks.autosave_confirmed 为 false 说明脚本没能观测到「已保存/Saved」提示——内容可能已粘贴但未必已持久化;warnings 会同步提示;不要仅凭 ok: true 判定成功,需人工打开 checks.draft_url 核实

脚本规格(scripts/)

脚本 职责 依赖
parsexarticle.py MD → JSON(标题/封面/图/分割线/HTML);改编自 wshuyi/x-article-publisher-skill(MIT,见仓库 THIRDPARTYNOTICES) stdlib
xarticlepost.py 宿主无关 Playwright 主路线:登录态复用(与 xpost.py 共用同一 profile)、Articles 入口探测(check:notloggedin/noarticles_access/ok/unknown)、全自动草稿(draft:封面→标题→正文→插图→分割线→机械校验)、跨平台剪贴板(Playwright 原生 Clipboard API,无 osascript);无任何发布代码路径 pip install playwright && python -m playwright install chromium
clipboard_x.py 备选路线专用:HTML/图片上 macOS 剪贴板(osascript;--max-bytes 超限自动降采样临时副本) stdlib + macOS osascript/sips

验证与测试

脚本层 fixture 前向测试(不需要 X 账号,改脚本后必跑,parsexarticle.py/clipboard_x.py 本次未改动,标准不变):

  1. 造一篇含 frontmatter title、文首封面图、2 张正文图(png+jpg)、--- 分割线、代码块、有序/无序列表、链接的测试文章。
  2. parsexarticle.py 断言:title 取自 frontmatter;coverimage 为绝对路径且指向文首图;contentimages[].blockindex 递增、aftertext 无 CODE 标记且已剥 markdown 记号;删掉文首图后 cover_image 为 null(闸门语义)。
  3. clipboard_x.py html 后 osascript -e 'clipboard info' 必须含 «class HTML»;image 对 png/jpg 分别产出 «class PNGf»/JPEG picture;--max-bytes 超限走 sips 临时副本且原文件字节数不变。

xarticlepost.py 经过一轮实现 + 三镜头独立对抗审查(安全 / 正确性 / 跨平台)后按发现修复:新增 waitforautosavesignal(插图与分割线全部处理完、正式关闭浏览器前,先等防抖自动保存生效再尝试观测「已保存/Saved」,结果写进 checks.autosaveconfirmed,观测不到时给出警告而不是默默继续——修复"关闭时机可能早于持久化,却仍报告 ok:true"的阻断级问题)、分割线锚点找不到时回退到正文编辑区(与正文图片一致,避免落在上一次操作残留的光标位置)、封面文件上传失败时降级为警告而非中止整份草稿、媒体数量校验改为限定 role="main" 区域内计数(避免导航栏头像等页面级 <img> 污染计数)。跨平台镜头审查无发现。

xarticlepost.py——本次 PR 合并前实际跑过的:

  1. python3 -m py_compile + ast.parse 语法校验通过;importlib 实际执行模块顶层代码(正则编译等)无异常。
  2. 人工审阅 + grep -in "publish\|发布" 全文核对:命中的都是注释/文档字符串/路径名,没有一处命中 .click()/.press() 调用——验证「没有任何发布代码路径」这条硬约束。
  3. status/check 已用真实 X 账号(本机共享登录态)实测通过:status 正确报告 loggedin: true;check 对一个已知未订阅 Premium 的账号正确返回 noarticlesaccess,message 字段与脚本 NOPREMIUMMESSAGE 常量逐字节核对一致;直接抓取该页面验证了分类命中路径——标题 未找到页面 / X、正文含「该页面不存在」,命中 NOTFOUNDTEXTMARKERS 里的「页面不存在」子串(走的是 body-text 匹配分支,不是标题 "404" 匹配,也不是碰运气命中 Premium-upsell 兜底分支)。

xarticlepost.py——本次 PR 合并前尚未跑、如实留给后续验证的(不打包成"已测试"):

  1. check 的 ok 分支与完整 draft 流程(封面上传、标题、正文富文本粘贴、按 blockindex 倒序插图、分割线菜单插入、autosaveconfirmed 观测、机械校验)——需要一个订阅含「撰写文章」权益的真实 Premium 账号,目前没有可用账号验证;findcreatecontrol / findtitleinput / findbodyeditor / SAVEDINDICATORRE / 分割线菜单等选择器全部是文本/角色定位的合理猜测,未在真实编辑器 DOM 上验证过,第一次真实跑很可能命中「X 编辑器改版找不到控件」或 autosave_confirmed: false 这类边界,需要按页面实况更新选择器与本文档。
  2. 跨平台剪贴板路径中 Windows/Linux 上的 navigator.clipboard.write + Control+V ——本次开发环境是 macOS,只验证了 sys.platform 平台判断逻辑本身,未在 Windows/Linux 真机上跑过。

浏览器端到端标准不变:以第 3 步机械校验五项全绿(含 autosave_confirmed: true)+ 草稿 URL 为通过标准;选择器因编辑器改版失效时,以页面实况为准更新本文档的选择器描述。

完成后回执

  1. 做了什么:一句话 + 草稿 URL。
  2. 校验清单:第 3 步逐项结果;未通过项如实标注。
  3. 下一步:人工审阅后自行发布;建议配 soia-media-generate-article-image 生成封面。