le-wechat-article v1.1:停点升级为回合终止契约(6 固定停点)+ 多账号支持(分账号画像 / --account 凭证)+ 文案通用化

This commit is contained in:
leroy
2026-10-06 21:02:24 +08:00
parent a484c8b46c
commit 0ba7c1f39d
6 changed files with 95 additions and 55 deletions
@@ -4,12 +4,16 @@
前置条件:
1. 已认证的公众号(未认证账号调用草稿接口会返回 48001)。
2. 环境变量 WECHAT_APP_ID、WECHAT_APP_SECRET(在 mp.weixin.qq.com 设置与开发→基本配置获取)。
3. 调用方公网 IP 已加入公众号后台的 IP 白名单(家用宽带 IP 变化后需回后台更新)。
2. 环境变量凭证(在 mp.weixin.qq.com 设置与开发→基本配置获取,只放 shell 环境变量,不进 git):
默认账号 WECHAT_APP_ID / WECHAT_APP_SECRET;
多账号按 --account <key> 读 WECHAT_<KEY>_APP_ID / WECHAT_<KEY>_APP_SECRET(key 转大写),
每个账号在各自后台配置,互不相通。
3. 调用方公网 IP 已加入**该账号**后台的 IP 白名单(家用宽带 IP 变化后需回后台更新)。
用法:
python3 publish_draft.py <文章目录> [--title 标题] [--digest 摘要] [--cover 封面图] \
[--check] [--dry-run] [--author 作者]
[--account 账号key] [--check] [--dry-run] [--author 作者]
--account 多账号选择,读 WECHAT_<KEY>_APP_ID / WECHAT_<KEY>_APP_SECRET;缺省用默认凭证
--check 只验证凭证与 IP 白名单(调 token 接口),不写任何内容
--dry-run 不联网,打印将上传的图片与将提交的草稿参数
退出码:0 成功;1 失败(错误码已翻译成人话);2 凭证缺失或参数不合法。
@@ -135,6 +139,25 @@ def get_token(app_id, secret):
return r["access_token"]
def load_creds(account):
"""按账号取凭证:--account key 读 WECHAT_<KEY>_APP_ID/_SECRET,缺省读默认对。"""
if account:
key = re.sub(r"[^A-Z0-9]+", "_", account.upper()).strip("_")
env_id, env_secret = f"WECHAT_{key}_APP_ID", f"WECHAT_{key}_APP_SECRET"
else:
env_id, env_secret = "WECHAT_APP_ID", "WECHAT_APP_SECRET"
return os.environ.get(env_id, ""), os.environ.get(env_secret, ""), env_id, env_secret
def missing_creds(env_id, env_secret):
print(f"❌ 缺少环境变量 {env_id} / {env_secret}\n"
" 获取路径:mp.weixin.qq.com(该账号)→ 设置与开发 → 基本配置\n"
f" 设置方式:export {env_id}=xxx {env_secret}=yyy(建议写进 ~/.zshrc)\n"
" 多账号:每个号各配一对,同步时用 --account <key> 选择。别把密钥写进任何 git 仓库。",
file=sys.stderr)
sys.exit(2)
def main():
ap = argparse.ArgumentParser(description="同步成稿到公众号草稿箱(不群发)")
ap.add_argument("dir", help="文章目录(含 article.html)")
@@ -142,24 +165,21 @@ def main():
ap.add_argument("--digest", default="")
ap.add_argument("--author", default="")
ap.add_argument("--cover", help="封面图路径(默认在文章目录找 cover.png/jpg/jpeg/webp)")
ap.add_argument("--account", default="", help="多账号选择:读 WECHAT_<KEY>_APP_ID/_SECRET;缺省用默认凭证")
ap.add_argument("--check", action="store_true", help="只验证凭证与 IP 白名单")
ap.add_argument("--no-crop", action="store_true", help="不提交封面裁剪框参数")
ap.add_argument("--dry-run", action="store_true", help="不联网,打印执行计划")
args = ap.parse_args()
app_id = os.environ.get("WECHAT_APP_ID", "")
secret = os.environ.get("WECHAT_APP_SECRET", "")
app_id, secret, env_id, env_secret = load_creds(args.account)
acct_note = f"账号 {args.account}" if args.account else "默认账号"
# --check 只验凭证与 IP,不需要文章产物
if args.check:
if not (app_id and secret):
print("❌ 缺少环境变量 WECHAT_APP_ID / WECHAT_APP_SECRET\n"
" 获取路径:mp.weixin.qq.com → 设置与开发 → 基本配置\n"
" 设置方式:export WECHAT_APP_ID=xxx WECHAT_APP_SECRET=yyy(建议写进 ~/.zshrc)\n"
" 别把密钥写进任何 git 仓库。", file=sys.stderr)
sys.exit(2)
missing_creds(env_id, env_secret)
get_token(app_id, secret) # 失败时内部已 die 并翻译错误码
print("✔ 凭证与 IP 白名单验证通过,可以同步草稿箱")
print(f"✔ {acct_note} 凭证与 IP 白名单验证通过,可以同步草稿箱")
return
# ---- 收集产物(dry-run 也走这段,但不联网) ----
@@ -196,11 +216,7 @@ def main():
return
if not (app_id and secret):
print("❌ 缺少环境变量 WECHAT_APP_ID / WECHAT_APP_SECRET\n"
" 获取路径:mp.weixin.qq.com → 设置与开发 → 基本配置\n"
" 设置方式:export WECHAT_APP_ID=xxx WECHAT_APP_SECRET=yyy(建议写进 ~/.zshrc)\n"
" 别把密钥写进任何 git 仓库。", file=sys.stderr)
sys.exit(2)
missing_creds(env_id, env_secret)
if not args.title:
die("正式同步必须提供 --title", 2)