Files
ai-proj-helper/skills-integration/wecom-plugin/skills/SKILL.md
T
johnQ 237631147a chore(skill): 清理全部 23 条历史校验警告
- 11 个超 20KB 的 SKILL.md 拆分,大章节移入 references/details.md
- 7 处非标准字段(arguments/user_invocable)移入 metadata
- 修 req-dev 4 条相对链接;validate 跳过行内代码避免 ![](...) 误报
- validate-skills.py: 23 warns -> 0 warns,0 errors
2026-10-31 22:21:07 +08:00

13 KiB
Raw Blame History

name, description, metadata, disable-model-invocation
name description metadata disable-model-invocation
wecom 企业微信集成。通过自然语言发送消息、管理群机器人、操作审批流程、管理通讯录。当用户提到企业微信、微信工作、群机器人、企业号、wecom相关任务时自动激活。
invocation
user-invoked
true

功能概述

消息推送

  • 应用消息: 向指定用户/部门发送文本、图片、文件等消息
  • 群机器人: 通过 Webhook 向群聊发送消息
  • 模板消息: 发送结构化的卡片消息

通讯录管理

  • 部门管理: 查询、创建、更新部门
  • 成员管理: 查询、创建、更新成员信息

审批流程

  • 发起审批: 通过 API 发起审批申请
  • 审批状态: 查询审批单状态

环境配置

已配置的企业微信应用

配置项 值
企业ID (CorpID) ww8ab927306fa235d2
应用ID (AgentId) 1000003
可信域名 wecom.pipexerp.com

环境变量

# ~/.zshrc 已配置
export WECOM_CORP_ID="ww8ab927306fa235d2"
export WECOM_AGENT_ID="1000003"
export WECOM_SECRET="Dts8BmENzjxCRK1Qn4qmkO6mU81FLVEkhI2LitcBcjI"

API 基础

获取 Access Token

import os
import requests

CORP_ID = os.environ.get("WECOM_CORP_ID")
CORP_SECRET = os.environ.get("WECOM_SECRET")
AGENT_ID = os.environ.get("WECOM_AGENT_ID")

def get_access_token():
    """获取企业微信 access_token(有效期 7200 秒)"""
    url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
    params = {
        "corpid": CORP_ID,
        "corpsecret": CORP_SECRET
    }
    resp = requests.get(url, params=params)
    result = resp.json()

    if result.get("errcode") == 0:
        return result["access_token"]
    else:
        raise Exception(f"获取 token 失败: {result}")

消息推送

发送文本消息

def send_text(user_id: str, content: str):
    """发送文本消息给指定用户"""
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}"

    data = {
        "touser": user_id,  # 用 "@all" 发送给所有人
        "msgtype": "text",
        "agentid": int(AGENT_ID),
        "text": {"content": content}
    }

    resp = requests.post(url, json=data)
    return resp.json()

# 使用示例
send_text("@all", "这是一条测试消息")

发送 Markdown 消息

def send_markdown(user_id: str, content: str):
    """发送 Markdown 格式消息"""
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}"

    data = {
        "touser": user_id,
        "msgtype": "markdown",
        "agentid": int(AGENT_ID),
        "markdown": {"content": content}
    }

    resp = requests.post(url, json=data)
    return resp.json()

发送卡片消息

def send_card(user_id: str, title: str, description: str, url: str):
    """发送文本卡片消息"""
    token = get_access_token()
    api_url = f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}"

    data = {
        "touser": user_id,
        "msgtype": "textcard",
        "agentid": int(AGENT_ID),
        "textcard": {
            "title": title,
            "description": description,
            "url": url,
            "btntxt": "详情"
        }
    }

    resp = requests.post(api_url, json=data)
    return resp.json()

群机器人 Webhook

def send_to_group(webhook_url: str, content: str, mentioned_list: list = None):
    """通过 Webhook 发送群消息"""
    data = {
        "msgtype": "text",
        "text": {
            "content": content,
            "mentioned_list": mentioned_list or []
        }
    }

    resp = requests.post(webhook_url, json=data)
    return resp.json()

# 使用示例
WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx"
send_to_group(WEBHOOK_URL, "自动化任务完成通知", ["@all"])

通讯录管理

获取部门列表

def get_departments():
    """获取部门列表"""
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token={token}"
    resp = requests.get(url)
    return resp.json()

获取部门成员

def get_department_users(department_id: int):
    """获取部门成员列表"""
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/user/simplelist"
    params = {"access_token": token, "department_id": department_id}
    resp = requests.get(url, params=params)
    return resp.json()

云文档操作

样式规范

企业微信云文档支持两种样式方案,根据文档类型选择:

文档类型 推荐方案 说明
合同、协议 带标题样式 使用 create_contract_doc()
报告、记录 简单文本 使用 create_simple_doc()
会议纪要 简单文本 使用 create_simple_doc()

简单文本样式符号规范

文档标题                        ← 由文档名称体现
━━━━━━━━━━━━━━━━━━━━━━━━━━━━    ← 分隔线
【章节标题】                    ← 用【】标记一级章节
内容正文...

【子章节】                      ← 同样用【】
1.1 条款内容
1.2 条款内容

带标题样式(合同类)

  • heading_level=1: 文档主标题(如"物流服务合同")
  • heading_level=2: 章节标题(如"第一条"、"甲方")
  • heading_level=0: 正文内容

创建文档

def create_doc(doc_name: str, doc_type: int = 3):
    """
    创建企业微信文档

    Args:
        doc_name: 文档名称
        doc_type: 文档类型 (3=文档, 4=表格)

    Returns:
        dict: {"docid": "xxx", "url": "https://doc.weixin.qq.com/..."}
    """
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token={token}"

    resp = requests.post(url, json={
        "doc_type": doc_type,
        "doc_name": doc_name
    })
    return resp.json()

# 使用示例
result = create_doc("项目文档")
print(f"文档链接: {result['url']}")

获取文档内容

def get_doc_content(docid: str):
    """获取文档内容"""
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/get?access_token={token}"

    resp = requests.post(url, json={"docid": docid})
    return resp.json()

编辑文档内容

def update_doc(docid: str, text: str, index: int = 0):
    """
    更新文档内容

    Args:
        docid: 文档ID
        text: 要插入的文本内容
        index: 插入位置 (0=开头)
    """
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"

    resp = requests.post(url, json={
        "docid": docid,
        "requests": [
            {
                "insert_text": {
                    "text": text,
                    "location": {"index": index}
                }
            }
        ]
    })
    return resp.json()

# 使用示例
update_doc("DOCID", "# 标题\n\n这是正文内容")

获取文档列表

def list_docs(limit: int = 20):
    """获取文档列表"""
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/doc_list?access_token={token}"

    resp = requests.post(url, json={"limit": limit})
    return resp.json()

上传图片到文档

import base64

def upload_doc_image(docid: str, image_path: str):
    """
    上传图片到文档(获取图片 URL)

    Args:
        docid: 文档ID
        image_path: 本地图片路径

    Returns:
        dict: {"url": "https://wdcdn.qpic.cn/...", "width": 1280, "height": 800}
    """
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/image_upload?access_token={token}"

    with open(image_path, "rb") as f:
        img_base64 = base64.b64encode(f.read()).decode()

    resp = requests.post(url, json={
        "docid": docid,
        "base64_content": img_base64
    })
    return resp.json()

# 使用示例
result = upload_doc_image("DOCID", "/tmp/screenshot.png")
print(f"图片 URL: {result['url']}")

插入图片到文档

def insert_image_to_doc(docid: str, image_url: str, index: int = 1):
    """
    插入图片到文档

    Args:
        docid: 文档ID
        image_url: 图片 URL(从 upload_doc_image 获取)
        index: 插入位置

    注意:使用 insert_paragraph + elements + image 格式
    """
    token = get_access_token()
    url = f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}"

    resp = requests.post(url, json={
        "docid": docid,
        "requests": [
            {
                "insert_paragraph": {
                    "elements": [
                        {"image": {"url": image_url}}
                    ],
                    "location": {"index": index}
                }
            }
        ]
    })
    return resp.json()

# 使用示例
insert_image_to_doc("DOCID", "https://wdcdn.qpic.cn/xxx", index=1)

获取文档末尾位置

def get_doc_end_index(docid: str) -> int:
    """获取文档末尾索引(用于追加内容)"""
    doc = get_doc_content(docid)

    if doc.get("errcode") != 0:
        return 1

    body = doc.get("document", {}).get("body", {})
    blocks = body.get("blocks", [])

    end_index = 1
    for block in blocks:
        if "paragraph" in block:
            for elem in block["paragraph"].get("elements", []):
                if "text_run" in elem:
                    end_index = max(end_index, elem["text_run"].get("end_index", 1))

    return end_index

完整示例:截图并插入文档

import base64
from playwright.sync_api import sync_playwright

def screenshot_and_insert(url: str, docid: str, title: str = "网页截图"):
    """
    截取网页并插入到文档

    Args:
        url: 要截取的网页 URL
        docid: 目标文档 ID
        title: 截图标题
    """
    token = get_access_token()

    # 1. 截取网页
    print(f"截取网页: {url}")
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page(viewport={"width": 1280, "height": 800})
        page.goto(url, wait_until="networkidle")
        page.screenshot(path="/tmp/screenshot.png")
        browser.close()

    # 2. 上传图片
    print("上传图片...")
    with open("/tmp/screenshot.png", "rb") as f:
        img_base64 = base64.b64encode(f.read()).decode()

    upload_resp = requests.post(
        f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/image_upload?access_token={token}",
        json={"docid": docid, "base64_content": img_base64}
    ).json()

    img_url = upload_resp.get("url")

    # 3. 获取文档末尾位置
    end_index = get_doc_end_index(docid)

    # 4. 插入标题
    requests.post(
        f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}",
        json={
            "docid": docid,
            "requests": [
                {"insert_text": {"text": f"\n\n{title}\n\n", "location": {"index": end_index}}}
            ]
        }
    )

    # 5. 插入图片
    new_end = get_doc_end_index(docid)
    insert_resp = requests.post(
        f"https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token={token}",
        json={
            "docid": docid,
            "requests": [
                {
                    "insert_paragraph": {
                        "elements": [{"image": {"url": img_url}}],
                        "location": {"index": new_end}
                    }
                }
            ]
        }
    ).json()

    if insert_resp.get("errcode") == 0:
        print("✅ 截图已插入文档!")
    else:
        print(f"❌ 插入失败: {insert_resp}")

# 使用示例
screenshot_and_insert("https://www.baidu.com", "YOUR_DOCID", "百度首页截图")

常见错误码

错误码 说明 解决方案
40001 access_token 无效 重新获取 token
40058 请求参数错误 检查 API 参数格式
48002 API 未授权 在应用设置中开启对应 API 权限
60011 用户不存在 检查 userid 是否正确
60020 IP 不在白名单 在应用设置中添加可信 IP
81013 缺少通讯录权限 在应用权限中开启通讯录读取权限
44001 文档不存在 检查 docid 是否正确
2050065 插入位置无效 使用 get_doc_end_index 获取正确位置
2400001 请求参数错误 检查 insert_paragraph 格式
93017 JSON 格式错误 图片上传需要使用 base64_content

相关资源

资源 链接
API 文档 https://developer.work.weixin.qq.com/document/
调试工具 https://developer.work.weixin.qq.com/devtool/interface
管理后台 https://work.weixin.qq.com/wework_admin/

详细内容

以下章节已移到 references/details.md:

  • 通用函数封装