Skill: chenjiahui11/weixin-wechat-channel (version 1.0.2)

--- name: wechat-public-auto description: "微信公众号自动发文一站式技能。组合 内容策划 + 人性化润色 + 自动创建草稿，一条龙生成公众号文章保存到草稿箱。Use when user needs to write 公众号文章、微信公众号、create wechat public account article, auto save to draft。" --- # 微信公众号自动发文一站式技能 整合了 **内容运营专家 + humanizer 人性化润色 + 微信公众号草稿箱** 三个能力，一站式完成从选题到存草稿。 ## 工作流程 1. **接收用户需求** - 用户给出文章主题 2. **内容策划** - 按照 `operations-expert` 内容策略确定受众、价值、目标、结构 3. **写作初稿** - 根据策划写出完整文章，markdown 格式，段落之间空行 4. **人性化润色** - 调用 `humanizer` 去除 AI 写作痕迹，让文字更自然 5. **自动创建草稿** - 自动生成/复用封面，创建草稿保存到微信公众号后台 6. **返回结果** - 告诉用户 Media ID 和成功信息，用户登录公众号就能发布 ## 格式要求 - markdown 写作，每个大段落之间必须空一行 - 标题用 `#`、`##` 标记 - 写完润色保证： - 去除 AI 套话（"pivotal role", "evolving landscape" 这类） - 变化句子长度，避免单调 - 去掉三连排比、虚假平行结构 - 去掉emoji、过度加粗 - 段落之间保证空行，HTML 输出每个块加 `<br>` 保证显示有空行 ## 依赖 - 需要环境变量 `WECHAT_APPID` `WECHAT_APPSECRET` 已配置 - 需要 IP 白名单已经添加正确 - 需要 `requests` `pillow` Python 依赖已安装 ## 购买卡密 | 类型 | 价格 | 链接 | |------|------|------| | 日卡 | 9.9元 | https://www.zhufaka.cn/item/tgcsid | | 月卡 | 399元 | https://www.zhufaka.cn/item/i6wfue | | 年卡 | 3500元 | https://www.zhufaka.cn/item/x8ancz | | 定制 | 联系 | 微信 CChenJ_ | ## 卡密（授权） 本技能采用**联网验证**模式，连接授权服务器校验卡密有效性。 1. **客户端激活**（首次运行）： ```text cd "<本技能文件夹>" py .\scripts\run_wechat_channel_ops.py --card-key "你的卡密" ``` 2. **校验入口**： ```text py .\scripts\run_wechat_channel_ops.py --card-key "你的卡密" py .\scripts\run_wechat_channel_ops.py --show-machine-id py .\scripts\run_wechat_channel_ops.py --license-status ``` 3. **授权服务器**：默认连接 `http://120.27.202.105:8000`，可通过环境变量 `TMO_LICENSE_SERVER` 覆盖。 ## 作者 组合技能 by OpenClaw session, 2026-03-17

path: README.md
content: 
# wechat-public-auto

微信公众号自动发文一站式技能

## 功能

整合 **内容运营专家 + humanizer 人性化润色 + 微信公众号草稿箱** 三个能力，一站式完成从选题到存草稿。

- 用户给主题 → 自动策划 → 写作 → 润色 → 排版 → 创建草稿 → 完成
- 自动去除 AI 写作痕迹，文章更自然
- 自动处理段落空格，微信显示美观
- 自动封面上传，开箱即用

## 工作流程

1. 用户给出文章主题
2. 按照内容运营策略确定受众、价值、结构
3. 写作初稿
4. humanizer 润色去除 AI 痕迹
5. 正确排版（段落空行）
6. 自动创建草稿保存到微信公众号后台
7. 用户登录公众号直接发布

## 要求

- 已经配置环境变量 `WECHAT_APPID` `WECHAT_APPSECRET`
- 微信公众号后台已经添加 IP 白名单
- Python 依赖 `requests` `pillow` 已安装

## 卡密

- 使用 `scripts/license_gate.py` + `scripts/run_wechat_channel_ops.py`，与淘宝商家技能相同的 `TMO1` 卡密与 `TMO_MASTER_SECRET`。
- 发卡请使用你已有的 `cardkey_issuer.py`（勿随客户包分发）。
- 详见 `SKILL.md` 中「卡密（授权）」一节。

## 作者

Created by: [你的名字] - 2026-03-17

## 许可证

MIT


path: scripts/license_gate.py
content: #!/usr/bin/env python3
"""
远程授权专用：仅通过授权服务器 /api/activate、/api/check 校验，不包含本地签名校验。
"""
from __future__ import annotations

import hashlib
import json
import os
import platform
import time
import uuid
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any

# 默认授权服务（可被环境变量 TMO_LICENSE_SERVER 覆盖）
_DEFAULT_LICENSE_SERVER = "http://120.27.202.105:8000"
_LICENSE_ENV = os.environ.get("TMO_LICENSE_SERVER")
LICENSE_SERVER_URL = (_LICENSE_ENV if _LICENSE_ENV is not None and _LICENSE_ENV.strip() != "" else _DEFAULT_LICENSE_SERVER).rstrip("/")


def machine_fingerprint() -> str:
    node = platform.node() or ""
    system = platform.system() or ""
    machine = platform.machine() or ""
    mac = str(uuid.getnode())
    raw = f"{node}|{system}|{machine}|{mac}".encode("utf-8", errors="ignore")
    return hashlib.sha256(raw).hexdigest()[:24]


def _load_json(path: Path) -> dict[str, Any] | None:
    if not path.exists():
        return None
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
        return data if isinstance(data, dict) else None
    except Exception:
        return None


def _save_json(path: Path, data: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")


def _fmt_ts(ts: int) -> str:
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(ts)))


class LicenseError(Exception):
    pass


def _http_post_json(url: str, payload: dict[str, Any], timeout: float = 10.0) -> dict[str, Any]:
    body = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        url,
        data=body,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            text = resp.read().decode("utf-8")
            data = json.loads(text or "{}")
            return data if isinstance(data, dict) else {}
    except urllib.error.HTTPError as exc:
        try:
            text = exc.read().decode("utf-8")
            data = json.loads(text or "{}")
            if isinstance(data, dict) and "detail" in data:
                raise LicenseError(f"授权服务器错误: {data['detail']}") from exc
        except Exception:
            pass
        raise LicenseError(f"授权服务器响应异常 (HTTP {exc.code})，请稍后重试。") from exc
    except urllib.error.URLError as exc:
        raise LicenseError("无法连接授权服务器，请检查服务器是否可访问或稍后重试。") from exc


def _remote_activate(card_key: str, machine_fp: str) -> dict[str, Any]:
    if not LICENSE_SERVER_URL:
        raise LicenseError("未配置授权服务器地址，请设置环境变量 TMO_LICENSE_SERVER。")
    url = f"{LICENSE_SERVER_URL}/api/activate"
    data = _http_post_json(url, {"card_key": card_key, "machine_fp": machine_fp})
    if (data.get("status") or "").lower() != "ok":
        msg = str(data.get("message") or "卡密激活失败，请确认卡密是否正确或联系卖家。")
        raise LicenseError(msg)
    return data


def _remote_check(machine_fp: str, card_key: str | None = None) -> dict[str, Any]:
    if not LICENSE_SERVER_URL:
        raise LicenseError("未配置授权服务器地址，请设置环境变量 TMO_LICENSE_SERVER。")
    url = f"{LICENSE_SERVER_URL}/api/check"
    payload: dict[str, Any] = {"machine_fp": machine_fp}
    if card_key:
        payload["card_key"] = card_key
    data = _http_post_json(url, payload)
    if (data.get("status") or "").lower() != "ok":
        msg = str(data.get("message") or "授权校验失败，请重新输入卡密或联系卖家。")
        raise LicenseError(msg)
    return data


def load_license_status(license_file: Path) -> dict[str, Any]:
    now = int(time.time())
    fp = machine_fingerprint()
    act = _load_json(license_file)
    if not act:
        return {
            "status": "missing",
            "license_file": str(license_file),
            "machine_fp": fp,
        }
    ver = int(act.get("version") or 0)
    if ver < 2:
        return {
            "status": "legacy_local",
            "license_file": str(license_file),
            "machine_fp": fp,
            "message": "检测到旧版本地授权文件，请删除该文件后使用服务器卡密重新激活。",
        }
    if str(act.get("machine_fp") or "") != fp:
        return {
            "status": "wrong_machine",
            "license_file": str(license_file),
            "machine_fp": fp,
            "bound_machine_fp": str(act.get("machine_fp") or ""),
        }
    expires_at = int(act.get("expires_at") or 0)
    if expires_at <= now:
        return {
            "status": "expired",
            "license_file": str(license_file),
            "machine_fp": fp,
            "expires_at": expires_at,
            "expires_at_text": _fmt_ts(expires_at),
            "plan": str(act.get("plan") or ""),
        }
    return {
        "status": "valid",
        "license_file": str(license_file),
        "machine_fp": fp,
        "expires_at": expires_at,
        "expires_at_text": _fmt_ts(expires_at),
        "activated_at": int(act.get("activated_at") or 0),
        "plan": str(act.get("plan") or ""),
    }


def ensure_license(license_file: Path, key_from_cli: str = "") -> dict[str, Any]:
    if not LICENSE_SERVER_URL:
        raise LicenseError("未配置授权服务器地址，请设置环境变量 TMO_LICENSE_SERVER。")

    now = int(time.time())
    fp = machine_fingerprint()
    act = _load_json(license_file)

    if act and int(act.get("version") or 0) < 2:
        raise LicenseError(
            "检测到旧版本地授权文件，本版本仅支持服务器卡密。\n"
            f"请手动删除后重新激活：\n  {license_file}"
        )

    if act and str(act.get("machine_fp") or "") == fp:
        try:
            remote = _remote_check(fp, card_key=str(act.get("key") or ""))
            act["plan"] = remote.get("plan") or act.get("plan") or "custom"
            expires = int(remote.get("expires_at") or act.get("expires_at") or now)
            act["expires_at"] = expires
            _save_json(license_file, act)
            return act
        except LicenseError:
            pass

    key = (key_from_cli or "").strip()
    if not key:
        print("未发现有效授权，请输入卡密（由卖家提供）：")
        key = input("Card Key: ").strip()
    if not key:
        raise LicenseError("未输入卡密，已退出。")

    remote = _remote_activate(key, fp)
    expires_at = int(remote.get("expires_at") or 0)
    if not expires_at or expires_at <= now:
        raise LicenseError("卡密已过期或授权异常，请联系卖家处理。")

    act = {
        "version": 2,
        "key": key,
        "plan": str(remote.get("plan") or "custom"),
        "machine_fp": fp,
        "activated_at": now,
        "expires_at": expires_at,
    }
    _save_json(license_file, act)
    print(f"授权激活成功，方案: {act['plan']}，到期: {_fmt_ts(act['expires_at'])}")
    return act


path: scripts/run_wechat_channel_ops.py
content: #!/usr/bin/env python3
from __future__ import annotations

"""
微信公众号技能入口：先校验卡密，再执行后续自动化。

联网验证模式：连接授权服务器校验卡密有效性。
"""

import argparse
import sys
from pathlib import Path

_SCRIPT_DIR = Path(__file__).resolve().parent
if str(_SCRIPT_DIR) not in sys.path:
    sys.path.insert(0, str(_SCRIPT_DIR))

from license_gate import ensure_license, machine_fingerprint, LicenseError


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(
        description="WeChat public account skill — license check (日/月/年卡)"
    )
    p.add_argument(
        "--license-file",
        default="",
        help="授权文件路径（默认：<本技能根目录>/license/license.json）",
    )
    p.add_argument(
        "--card-key",
        default="",
        help="首次激活时传入卡密（无授权文件且未传此参数时会提示输入）",
    )
    p.add_argument(
        "--show-machine-id",
        action="store_true",
        help="仅打印本机指纹（用于排查换机等问题）后退出",
    )
    p.add_argument(
        "--license-status",
        action="store_true",
        help="查看当前授权状态",
    )
    return p.parse_args()


def main() -> int:
    args = parse_args()
    skill_root = Path(__file__).resolve().parents[1]
    default_license = skill_root / "license" / "license.json"
    license_file = Path(args.license_file).resolve() if args.license_file else default_license

    if args.show_machine_id:
        print("Machine ID:", machine_fingerprint())
        return 0

    try:
        activation = ensure_license(license_file, key_from_cli=args.card_key)
        print(f"License OK — file: {license_file}")
        print(f"Plan: {activation.get('plan')}  expires_at: {activation.get('expires_at')}")
        return 0
    except LicenseError as exc:
        print(f"[ERROR] {exc}")
        raise SystemExit(1)


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except KeyboardInterrupt:
        print("\n[ABORT] 已取消")
        raise SystemExit(1)


path: _meta.json
content: ownerId: kn7f1x2403c6cb9854ym5syfz5832zq2
slug: weixin-wechat-channel
version: 1.0.2
publishedAt: 1773757990684

Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)