关于 medium
通过用户 Cookie 在 Medium 上读取和发布内容的命令行技能。它调用 Medium 内部 Web API,无需浏览器或第三方依赖,并要求确认后才能发布。
主要功能
- 01检查账号身份
- 02列出文章与互动数据
- 03查看文章详情
- 04以草稿或公开文章形式发布 Markdown
- 05将 Markdown 格式转换为 Medium 排版
适用场景
- 01查看已发布文章及互动数据
- 02准备 Medium 草稿
- 03发布经批准的文章
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ acedatacloud/ skills --skill medium安装后在 Claude Code 中输入/medium确认已加载。
附带文件scripts/medium.py
详细介绍
medium — 使用你自己的 Cookie 在 Medium 上阅读和发布
通过网站使用的同一套内部 Web API 操作用户的真实 Medium 账号(Medium 已于 2023 年弃用其公开写入 API),使用通过 ACE 扩展获取的登录 Cookie 进行身份验证。无需浏览器,也无需第三方依赖——只需 urllib。
连接器会将 Cookie jar 作为环境变量注入:
MEDIUM_COOKIES— Cookie 数组的 JSON(sid、uid、xsrf)。这是机密信息——绝不要回显或打印。 CLI 会在写入时自动将xsrfCookie 作为x-xsrf-token标头回显。如果获取的 Cookie jar 中没有xsrfCookie(很常见),CLI 会在写入前自动生成一个,因此发布时不再会因“Missing xsrf token”而失败。
CLI
该技能提供 scripts/medium.py——仅使用标准库的独立脚本。
# $SKILL_DIR can point at another skill loaded this turn — anchor on our own
# script, and re-run this at the top of every Bash block (fresh shell each time).
MED="$SKILL_DIR/scripts/medium.py"; [ -f "$MED" ] || MED=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/medium.py' 2>/dev/null | head -1)
[ -f "$MED" ] || { echo "medium script not found (SKILL_DIR=$SKILL_DIR)" >&2; exit 1; }
python3 "$MED" whoami # who is logged in
python3 "$MED" articles --limit 20 # my posts + clap/response stats
python3 "$MED" article <post-id> # one post's details
首先验证连接
MED="$SKILL_DIR/scripts/medium.py"; [ -f "$MED" ] || MED=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/medium.py' 2>/dev/null | head -1)
python3 "$MED" whoami
# → {"user_id": "...", "name": "...", "username": "..."}
如果出现身份验证错误,说明 Cookie 已过期——请让用户前往 https://studio.acedata.cloud/console/connectors 重新连接。不要循环重试。
发布——需确认(除非末尾带有 --confirm,否则为试运行)
publish 会写入用户的真实账号。内容使用 Markdown(转换为 Medium 段落块:#/##→标题,###→副标题,>→引用,
```→code, -/*→bullet list, 1.→numbered list, tables→aligned
code block). Inline bold, italic, code, and [links](url) render as
real Medium formatting (clickable links included). Bare https://… URLs in text
are auto-linked too (URLs inside code spans are left untouched).
Without a trailing --confirm it dry-runs. --confirm is honored only as the
last argument. Always show the dry-run, get an explicit "yes", then re-run.
MED="$SKILL_DIR/scripts/medium.py"; [ -f "$MED" ] || MED=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/medium.py' 2>/dev/null | head -1)
python3 "$MED" publish --title "Title" --content-file a.md # dry-run
python3 "$MED" publish --title "Title" --content-file a.md --draft-only --confirm # private draft
python3 "$MED" publish --title "Title" --content-file a.md --confirm # PUBLIC story
Publishing is Medium's multi-step editor flow (new-story → write deltas →
publish). --draft-only stops at the draft (visible only at the user's
/me/stories/drafts). Default to --draft-only unless the user asked to go live.
Images
publish automatically uploads each external markdown image () to
Medium and inserts it as a real image block — Medium has no markdown-image
syntax, so this is the only way images render. Pass --no-rehost-images to
degrade images to link-only paragraphs. An image that fails to upload falls back
to a link paragraph (never blocks the post).
Gotchas
- This is the user's real Medium account. Confirm before any publish.
- Markdown→Medium conversion covers headings, quotes, fenced code, bullet/
numbered lists, images, and inline markups (bold/italic/code/links). Medium's
editor has no table element: a narrow markdown table renders as an aligned
monospace code block, while a wide one (long cells / URLs, which would wrap into
an unreadable grid) is rendered as per-row records — the first column bolded as
a lead-in, the remaining columns as
header: valuebullets. - Medium sits behind Cloudflare; an occasional 403/429 is transient — the CLI auto-retries once after a short pause. A persistent 403 means the cookie is genuinely expired (reconnect).
- Never print
MEDIUM_COOKIES— it is full account access. - ToS: acts only on the user's own account with their own captured cookie.
Record the output
After you successfully publish and obtain the live result URL, call the built-in
publish_artifact tool ONCE so the user can track this deliverable in My Outputs:
publish_artifact(kind="article", channel="medium", title="<title>", url="<the REAL returned URL>", status="delivered")
Use the real returned URL — never fabricate one. Call it once per published item,
only after delivery is confirmed; skip it (or use status="failed") if publishing failed.
See _shared/artifacts.md.