Skill: jackyken/social-recruit-page (version 1.0.0)

--- name: recruit-page description: 生成高端黑金风格私人圈子/社群招募长图并发送到飞书。当用户说"生成招募页"、"做一个招募图"、"私人圈子招募页"、"帮我生成会员招募海报"、或提到需要私人俱乐部/高端社群/付费圈子的招募宣传图时触发。支持自定义品牌名、发起人、服务内容、定价、名额等信息。 --- # Recruit Page Generator 生成黑金风格竖版招募长图（750px 宽），适用于私人圈子、付费社群、高端俱乐部等场景。 ## 核心脚本 ``` scripts/generate.py ``` ## 快速使用 ### 最简调用（全用默认值，谢导私人圈子） ```bash python3 scripts/generate.py --output /tmp/recruit.png ``` ### 自定义配置 创建 `config.json`，然后： ```bash python3 scripts/generate.py --config config.json --output /tmp/recruit.png ``` ### 生成 + 直接发飞书 ```bash python3 scripts/generate.py \ --config config.json \ --output /tmp/recruit.png \ --send-feishu \ --receive-id ou_xxx \ --app-id cli_xxx \ --app-secret xxx ``` `--receive-id-type` 默认 `open_id`，群聊传 `chat_id`。 ## config.json 结构 ```json { "brand_name": "谢导私人圈子", "tagline": "AI 时代的企业家私人智囊团", "badge": "PRIVATE CIRCLE · 私人圈子", "hero_intro": "你不缺信息，你缺的是<strong>一个真正懂你的人</strong>在身边。", "founder_name": "谢导", "founder_emoji": "🔍", "founder_tags": ["AI 产品专家", "开源 LLM 业务增长", "心光年创始人", "2000+ 学员"], "founder_intro": "负责开源 LLM 商业增长，创办<strong>心光年觉醒读书会</strong>，7.2 万粉丝。", "price": "9,980", "price_unit": "¥", "quota": "30", "perks": [ "每月私董会共创（限 8 人）", "年内私聊直通车 · 24h 响应", "谢导背书的资源精准撮合", "心光年全年共读资料库" ], "services": [ { "title": "私董会共创", "tag": "每月一次 · 深度共创", "desc": "谢导带领圈子成员，每月拆解一个<strong>真实 AI 增长案例</strong>。" }, { "title": "私聊直通车", "tag": "年内随时 · 24h 响应", "desc": "随时向谢导提一个问题，<strong>24 小时内必回</strong>。" }, { "title": "资源网络撮合", "tag": "谢导背书 · 精准对接", "desc": "谢导亲自审核每位成员，<strong>定向撮合</strong>你需要的资源。" } ], "for_who": [ {"icon": "🚀", "title": "想用 AI 做增长的创业者", "desc": "知道 AI 很重要，但不知道从哪里切入"}, {"icon": "💡", "title": "在某个领域有深厚积累", "desc": "有专业认知，但缺少高密度的同频交流"}, {"icon": "🔗", "title": "需要精准资源对接的人", "desc": "不缺人脉，但缺少真正有质量的引荐"}, {"icon": "🎯", "title": "处于关键决策节点", "desc": "面临重大选择，需要一针见血的外部视角"} ], "footer_quote": "真正的价值，<strong>不在于你认识多少人</strong>，<br>而在于在关键时刻，<strong>有人愿意为你背书</strong>。", "year": "2026" } ``` 所有字段均可选，未传的使用内置默认值（谢导私人圈子版本）。 ## 飞书发图流程（channel=feishu 时） 脚本内置飞书发送逻辑（`--send-feishu`），也可手动： ```bash # 1. 获取 token（从 ~/.openclaw/openclaw.json 读取 channels.feishu.appId/appSecret） # 2. 上传图片 → 获取 image_key # 3. 发送 image 类型消息 ``` 详见 `scripts/generate.py` 中的 `send_feishu()` 函数。 ## 依赖 - `playwright`（已安装） - `curl`（系统自带） - Python 3.10+ ## 页面结构 固定 7 个区块（顺序不可变）： 1. **Hero** — 品牌名 + Tagline + 开场白 2. **创始人** — 头像 + 标签 + 简介 3. **为谁设计** — 2×2 网格，4 种目标人群 4. **核心服务** — 1-N 条服务（推荐 3 条） 5. **定价** — 年费 + 权益清单 + 名额 6. **入圈流程** — 固定 4 步（提交→审阅→面谈→入圈） 7. **底部** — 金句 + CTA 按钮 + 品牌签名

path: assets/template.html
content: <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>谢导私人圈子</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700;900&family=Noto+Sans+SC:wght@300;400;500;700&display=swap');

  * { margin: 0; padding: 0; box-sizing: border-box; }

  body {
    background: #0a0a0a;
    color: #e8d5b0;
    font-family: 'Noto Sans SC', sans-serif;
    width: 750px;
    margin: 0 auto;
  }

  /* ── 顶部Banner ── */
  .hero {
    background: linear-gradient(160deg, #1a1208 0%, #0d0d0d 40%, #0a0a0a 100%);
    padding: 80px 60px 60px;
    text-align: center;
    border-bottom: 1px solid #2a2010;
    position: relative;
    overflow: hidden;
  }
  .hero::before {
    content: '';
    position: absolute;
    top: -100px; left: -100px; right: -100px;
    height: 300px;
    background: radial-gradient(ellipse at center, rgba(212,175,55,0.12) 0%, transparent 70%);
  }
  .hero-badge {
    display: inline-block;
    border: 1px solid #c9a227;
    color: #c9a227;
    font-size: 13px;
    letter-spacing: 4px;
    padding: 6px 20px;
    margin-bottom: 36px;
    font-family: 'Noto Serif SC', serif;
  }
  .hero-title {
    font-family: 'Noto Serif SC', serif;
    font-size: 52px;
    font-weight: 900;
    color: #f0e0a0;
    line-height: 1.2;
    margin-bottom: 16px;
    text-shadow: 0 0 40px rgba(212,175,55,0.3);
  }
  .hero-title span {
    color: #c9a227;
  }
  .hero-subtitle {
    font-size: 18px;
    color: #9a8060;
    letter-spacing: 2px;
    margin-bottom: 48px;
    font-weight: 300;
  }
  .hero-divider {
    width: 60px;
    height: 2px;
    background: linear-gradient(90deg, transparent, #c9a227, transparent);
    margin: 0 auto 48px;
  }
  .hero-intro {
    font-size: 16px;
    line-height: 2;
    color: #c8b080;
    max-width: 560px;
    margin: 0 auto;
    font-weight: 300;
  }
  .hero-intro strong {
    color: #e8d070;
    font-weight: 500;
  }

  /* ── 创始人 ── */
  .founder {
    padding: 64px 60px;
    background: #080808;
    border-bottom: 1px solid #1e1810;
  }
  .section-label {
    font-size: 11px;
    letter-spacing: 5px;
    color: #6a5830;
    text-transform: uppercase;
    margin-bottom: 40px;
    text-align: center;
  }
  .founder-card {
    display: flex;
    gap: 36px;
    align-items: flex-start;
  }
  .founder-avatar {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    border: 2px solid #c9a227;
    background: linear-gradient(135deg, #2a1f08, #1a1408);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 36px;
    flex-shrink: 0;
    box-shadow: 0 0 20px rgba(201,162,39,0.2);
  }
  .founder-info h3 {
    font-family: 'Noto Serif SC', serif;
    font-size: 24px;
    color: #f0e0a0;
    margin-bottom: 8px;
  }
  .founder-info .title-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 20px;
  }
  .tag {
    background: rgba(201,162,39,0.1);
    border: 1px solid rgba(201,162,39,0.3);
    color: #c9a227;
    font-size: 12px;
    padding: 4px 12px;
    border-radius: 2px;
    letter-spacing: 1px;
  }
  .founder-info p {
    font-size: 15px;
    color: #9a8060;
    line-height: 1.9;
    font-weight: 300;
  }
  .founder-info p strong {
    color: #c8b080;
  }

  /* ── 为谁设计 ── */
  .for-who {
    padding: 64px 60px;
    background: linear-gradient(180deg, #0a0a0a 0%, #0d0b08 100%);
    border-bottom: 1px solid #1e1810;
  }
  .section-title {
    font-family: 'Noto Serif SC', serif;
    font-size: 30px;
    color: #f0e0a0;
    text-align: center;
    margin-bottom: 12px;
  }
  .section-title span { color: #c9a227; }
  .section-desc {
    text-align: center;
    color: #7a6840;
    font-size: 14px;
    margin-bottom: 48px;
    letter-spacing: 1px;
  }
  .who-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }
  .who-item {
    background: rgba(201,162,39,0.04);
    border: 1px solid rgba(201,162,39,0.15);
    padding: 24px 28px;
    border-radius: 2px;
  }
  .who-item .icon { font-size: 24px; margin-bottom: 12px; }
  .who-item h4 {
    font-size: 15px;
    color: #e8d5b0;
    margin-bottom: 8px;
    font-weight: 500;
  }
  .who-item p {
    font-size: 13px;
    color: #7a6840;
    line-height: 1.7;
  }

  /* ── 核心服务 ── */
  .services {
    padding: 64px 60px;
    background: #080808;
    border-bottom: 1px solid #1e1810;
  }
  .service-item {
    display: flex;
    gap: 28px;
    margin-bottom: 48px;
    padding-bottom: 48px;
    border-bottom: 1px solid #1a1408;
  }
  .service-item:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
  }
  .service-num {
    font-family: 'Noto Serif SC', serif;
    font-size: 48px;
    color: rgba(201,162,39,0.15);
    line-height: 1;
    flex-shrink: 0;
    width: 60px;
    font-weight: 900;
  }
  .service-content h3 {
    font-family: 'Noto Serif SC', serif;
    font-size: 22px;
    color: #f0e0a0;
    margin-bottom: 8px;
  }
  .service-content .service-tag {
    display: inline-block;
    background: rgba(201,162,39,0.12);
    color: #c9a227;
    font-size: 11px;
    padding: 3px 10px;
    margin-bottom: 16px;
    letter-spacing: 2px;
  }
  .service-content p {
    font-size: 15px;
    color: #8a7050;
    line-height: 1.9;
    font-weight: 300;
  }
  .service-content p strong {
    color: #c8b080;
  }

  /* ── 价格 ── */
  .pricing {
    padding: 64px 60px;
    background: linear-gradient(160deg, #0f0c04 0%, #0a0a0a 100%);
    border-bottom: 1px solid #1e1810;
    text-align: center;
  }
  .price-box {
    border: 1px solid rgba(201,162,39,0.4);
    padding: 56px 40px;
    max-width: 480px;
    margin: 0 auto 40px;
    position: relative;
    background: rgba(201,162,39,0.03);
  }
  .price-box::before {
    content: '首期限定';
    position: absolute;
    top: -1px; right: 32px;
    background: #c9a227;
    color: #0a0a0a;
    font-size: 11px;
    letter-spacing: 2px;
    padding: 4px 14px;
    font-weight: 700;
  }
  .price-amount {
    font-family: 'Noto Serif SC', serif;
    font-size: 72px;
    font-weight: 900;
    color: #c9a227;
    line-height: 1;
    margin-bottom: 8px;
  }
  .price-amount .currency {
    font-size: 32px;
    vertical-align: top;
    margin-top: 14px;
    display: inline-block;
  }
  .price-period {
    font-size: 14px;
    color: #6a5830;
    letter-spacing: 2px;
    margin-bottom: 36px;
  }
  .price-divider {
    width: 40px;
    height: 1px;
    background: rgba(201,162,39,0.3);
    margin: 0 auto 36px;
  }
  .price-perks {
    list-style: none;
    text-align: left;
  }
  .price-perks li {
    font-size: 15px;
    color: #9a8060;
    padding: 10px 0;
    border-bottom: 1px solid rgba(201,162,39,0.08);
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .price-perks li:last-child { border-bottom: none; }
  .price-perks li::before {
    content: '✦';
    color: #c9a227;
    font-size: 10px;
    flex-shrink: 0;
  }
  .quota-note {
    font-size: 14px;
    color: #5a4820;
    letter-spacing: 1px;
  }
  .quota-note strong {
    color: #c9a227;
    font-size: 20px;
    font-family: 'Noto Serif SC', serif;
  }

  /* ── 筛选 ── */
  .filter {
    padding: 64px 60px;
    background: #080808;
    border-bottom: 1px solid #1e1810;
  }
  .filter-steps {
    margin-top: 40px;
  }
  .filter-step {
    display: flex;
    gap: 24px;
    margin-bottom: 32px;
    align-items: flex-start;
  }
  .step-num {
    width: 40px;
    height: 40px;
    border: 1px solid #c9a227;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: 'Noto Serif SC', serif;
    font-size: 16px;
    color: #c9a227;
    flex-shrink: 0;
  }
  .step-content h4 {
    font-size: 16px;
    color: #e8d5b0;
    margin-bottom: 6px;
    padding-top: 8px;
    font-weight: 500;
  }
  .step-content p {
    font-size: 13px;
    color: #6a5830;
    line-height: 1.7;
  }

  /* ── 底部 ── */
  .footer {
    padding: 56px 60px;
    background: linear-gradient(180deg, #0a0a0a 0%, #050503 100%);
    text-align: center;
  }
  .footer-quote {
    font-family: 'Noto Serif SC', serif;
    font-size: 20px;
    color: #8a7050;
    line-height: 1.8;
    margin-bottom: 48px;
    font-style: italic;
  }
  .footer-quote strong { color: #c9a227; }
  .cta-btn {
    display: inline-block;
    border: 1px solid #c9a227;
    color: #0a0a0a;
    background: #c9a227;
    font-size: 15px;
    letter-spacing: 4px;
    padding: 16px 60px;
    margin-bottom: 24px;
    font-weight: 700;
    cursor: pointer;
  }
  .cta-note {
    font-size: 12px;
    color: #4a3818;
    letter-spacing: 1px;
    margin-bottom: 48px;
  }
  .footer-brand {
    font-size: 12px;
    color: #3a2c12;
    letter-spacing: 3px;
  }
  .footer-brand span { color: #5a4820; }
</style>
</head>
<body>

<!-- ── Hero ── -->
<div class="hero">
  <div class="hero-badge">{{BADGE}}</div>
  <div class="hero-title">{{BRAND_NAME}}</div>
  <div class="hero-subtitle">{{TAGLINE}}</div>
  <div class="hero-divider"></div>
  <div class="hero-intro">
    你不缺信息，你缺的是<strong>一个真正懂你的人</strong>在身边。<br>
    一个帮你把 AI 变成真实增长的人，<br>
    一个在你迷茫时能给你<strong>一针见血的判断</strong>的人。
  </div>
</div>

<!-- ── 创始人 ── -->
<div class="founder">
  <div class="section-label">FOUNDER · 发起人</div>
  <div class="founder-card">
    <div class="founder-avatar">🔍</div>
    <div class="founder-info">
      <h3>谢导</h3>
      <div class="title-tags">
        <span class="tag">AI 产品专家</span>
        <span class="tag">开源 LLM 业务增长</span>
        <span class="tag">心光年创始人</span>
        <span class="tag">2000+ 学员</span>
      </div>
      <p>
        负责开源 LLM 商业增长，亲历 AI 从 0 到 1 的完整链路。<br>
        创办<strong>心光年觉醒读书会</strong>，7.2 万粉丝，2000+ 学员，<br>
        同时是一个在市场里真金白银实战的创业者。<br><br>
        不卖课，不卖人设。<strong>只做真正有价值的连接。</strong>
      </p>
    </div>
  </div>
</div>

<!-- ── 为谁设计 ── -->
<div class="for-who">
  <div class="section-label">FOR WHO · 为谁设计</div>
  <div class="section-title">这个圈子，<span>不是为所有人</span>准备的</div>
  <div class="section-desc">以下四种人，你至少占一条</div>
  <div class="who-grid">
    <div class="who-item">
      <div class="icon">🚀</div>
      <h4>想用 AI 做增长的创业者</h4>
      <p>知道 AI 很重要，但不知道从哪里切入，怎么落地</p>
    </div>
    <div class="who-item">
      <div class="icon">💡</div>
      <h4>在某个领域有深厚积累</h4>
      <p>有专业认知，但缺少高密度的同频交流和碰撞</p>
    </div>
    <div class="who-item">
      <div class="icon">🔗</div>
      <h4>需要精准资源对接的人</h4>
      <p>不缺人脉，但缺少真正有质量的引荐和背书</p>
    </div>
    <div class="who-item">
      <div class="icon">🎯</div>
      <h4>处于关键决策节点</h4>
      <p>面临重大选择，需要一个能一针见血的外部视角</p>
    </div>
  </div>
</div>

<!-- ── 核心服务 ── -->
<div class="services">
  <div class="section-label">SERVICES · 核心服务</div>
  <div class="section-title" style="margin-bottom:48px">三件事，<span>真正有用</span></div>

  <div class="service-item">
    <div class="service-num">01</div>
    <div class="service-content">
      <h3>私董会共创</h3>
      <span class="service-tag">每月一次 · 深度共创</span>
      <p>
        谢导带领圈子成员，每月拆解一个<strong>真实 AI 增长案例</strong>。<br>
        不是讲课，是实战复盘。你带着问题来，带着答案走。<br>
        每次限定 8 人，确保每个人都能被认真回应。
      </p>
    </div>
  </div>

  <div class="service-item">
    <div class="service-num">02</div>
    <div class="service-content">
      <h3>私聊直通车</h3>
      <span class="service-tag">年内随时 · 24h 响应</span>
      <p>
        会员期内，你可以<strong>随时向谢导提一个问题</strong>，24 小时内必回。<br>
        不是群里问、不是公开咨询，是真正的一对一。<br>
        一个好问题，可能值百万。
      </p>
    </div>
  </div>

  <div class="service-item">
    <div class="service-num">03</div>
    <div class="service-content">
      <h3>资源网络撮合</h3>
      <span class="service-tag">谢导背书 · 精准对接</span>
      <p>
        圈子里的每个人，谢导都亲自审核过。<br>
        你需要什么资源，谢导帮你在圈内<strong>定向撮合</strong>，<br>
        不是随机拉群，是真正的「我觉得你们应该认识」。
      </p>
    </div>
  </div>
</div>

<!-- ── 定价 ── -->
<div class="pricing">
  <div class="section-label">PRICING · 年费</div>
  <div class="section-title" style="margin-bottom:48px"><span>一个决定</span>，改变一年的质量</div>

  <div class="price-box">
    <div class="price-amount">
      <span class="currency">¥</span>9,980
    </div>
    <div class="price-period">/ 年 · PER YEAR</div>
    <div class="price-divider"></div>
    <ul class="price-perks">
      <li>每月私董会共创（限 8 人）</li>
      <li>年内私聊直通车 · 24h 响应</li>
      <li>谢导背书的资源精准撮合</li>
      <li>圈子内部动态 · 优质信息流</li>
      <li>心光年全年共读资料库</li>
    </ul>
  </div>
  <div class="quota-note">
    首期仅招募 <strong>30</strong> 人 · 名额满即关闭
  </div>
</div>

<!-- ── 入圈流程 ── -->
<div class="filter">
  <div class="section-label">PROCESS · 入圈流程</div>
  <div class="section-title" style="margin-bottom:8px">不是<span>所有人</span>都能进</div>
  <div class="section-desc">谢导亲自面试，确保每一位成员都值得认识</div>

  <div class="filter-steps">
    <div class="filter-step">
      <div class="step-num">01</div>
      <div class="step-content">
        <h4>提交申请</h4>
        <p>填写简单表格：你是谁、你在做什么、你希望从圈子里得到什么</p>
      </div>
    </div>
    <div class="filter-step">
      <div class="step-num">02</div>
      <div class="step-content">
        <h4>谢导审阅</h4>
        <p>谢导亲自阅读每一份申请，3 个工作日内给出回复</p>
      </div>
    </div>
    <div class="filter-step">
      <div class="step-num">03</div>
      <div class="step-content">
        <h4>一对一面谈</h4>
        <p>30 分钟视频通话，谢导想了解你，也给你机会了解圈子</p>
      </div>
    </div>
    <div class="filter-step">
      <div class="step-num">04</div>
      <div class="step-content">
        <h4>正式入圈</h4>
        <p>双向确认后完成年费，正式成为成员，立即享受所有权益</p>
      </div>
    </div>
  </div>
</div>

<!-- ── Footer ── -->
<div class="footer">
  <div class="footer-quote">
    "真正的价值，<strong>不在于你认识多少人</strong>，<br>
    而在于在关键时刻，<strong>有人愿意为你背书</strong>。"
  </div>
  <div class="cta-btn">申请入圈</div>
  <div class="cta-note">提交申请 · 无需付费 · 双向选择</div>
  <div class="footer-brand">
    谢导私人圈子 · PRIVATE CIRCLE <span>© 2026</span>
  </div>
</div>

</body>
</html>


path: scripts/generate.py
content: #!/usr/bin/env python3
"""
recruit-page 生成脚本
用法：
  python3 generate.py                          # 用默认配置生成
  python3 generate.py --config config.json     # 用 JSON 配置生成
  python3 generate.py --output /tmp/out.png    # 指定输出路径
  python3 generate.py --send-feishu --receive-id ou_xxx --app-id xxx --app-secret xxx
"""

import argparse
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path


def build_html(c: dict) -> str:
    brand     = c.get("brand_name", "私人圈子")
    tagline   = c.get("tagline", "AI 时代的企业家私人智囊团")
    badge     = c.get("badge", "PRIVATE CIRCLE · 私人圈子")
    hero_intro = c.get("hero_intro", "你不缺信息，你缺的是<strong>一个真正懂你的人</strong>在身边。")
    f_name    = c.get("founder_name", "谢导")
    f_emoji   = c.get("founder_emoji", "🔍")
    f_intro   = c.get("founder_intro", "")
    f_tags    = c.get("founder_tags", [])
    price     = c.get("price", "9,980")
    unit      = c.get("price_unit", "¥")
    quota     = c.get("quota", "30")
    footer_q  = c.get("footer_quote", "真正的价值，不在于你认识多少人。")
    year      = c.get("year", "2026")
    services  = c.get("services", [])
    for_who   = c.get("for_who", [])
    perks     = c.get("perks", [])

    tags_html = "".join(f'<span class="tag">{t}</span>' for t in f_tags)

    services_html = ""
    for i, s in enumerate(services, 1):
        services_html += f"""
        <div class="service-item">
          <div class="service-num">0{i}</div>
          <div class="service-content">
            <h3>{s['title']}</h3>
            <span class="service-tag">{s.get('tag','')}</span>
            <p>{s['desc']}</p>
          </div>
        </div>"""

    who_html = ""
    for w in for_who:
        who_html += f"""
        <div class="who-item">
          <div class="icon">{w['icon']}</div>
          <h4>{w['title']}</h4>
          <p>{w['desc']}</p>
        </div>"""

    perks_html = "".join(f"<li>{p}</li>" for p in perks)

    return f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{brand}</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700;900&family=Noto+Sans+SC:wght@300;400;500;700&display=swap');
  *{{margin:0;padding:0;box-sizing:border-box}}
  body{{background:#0a0a0a;color:#e8d5b0;font-family:'Noto Sans SC',sans-serif;width:750px;margin:0 auto}}
  .hero{{background:linear-gradient(160deg,#1a1208 0%,#0d0d0d 40%,#0a0a0a 100%);padding:80px 60px 60px;text-align:center;border-bottom:1px solid #2a2010;position:relative;overflow:hidden}}
  .hero::before{{content:'';position:absolute;top:-100px;left:-100px;right:-100px;height:300px;background:radial-gradient(ellipse at center,rgba(212,175,55,.12) 0%,transparent 70%)}}
  .hero-badge{{display:inline-block;border:1px solid #c9a227;color:#c9a227;font-size:13px;letter-spacing:4px;padding:6px 20px;margin-bottom:36px;font-family:'Noto Serif SC',serif}}
  .hero-title{{font-family:'Noto Serif SC',serif;font-size:52px;font-weight:900;color:#f0e0a0;line-height:1.2;margin-bottom:16px;text-shadow:0 0 40px rgba(212,175,55,.3)}}
  .hero-title span{{color:#c9a227}}
  .hero-subtitle{{font-size:18px;color:#9a8060;letter-spacing:2px;margin-bottom:48px;font-weight:300}}
  .hero-divider{{width:60px;height:2px;background:linear-gradient(90deg,transparent,#c9a227,transparent);margin:0 auto 48px}}
  .hero-intro{{font-size:16px;line-height:2;color:#c8b080;max-width:560px;margin:0 auto;font-weight:300}}
  .hero-intro strong{{color:#e8d070;font-weight:500}}
  .founder{{padding:64px 60px;background:#080808;border-bottom:1px solid #1e1810}}
  .section-label{{font-size:11px;letter-spacing:5px;color:#6a5830;text-transform:uppercase;margin-bottom:40px;text-align:center}}
  .founder-card{{display:flex;gap:36px;align-items:flex-start}}
  .founder-avatar{{width:100px;height:100px;border-radius:50%;border:2px solid #c9a227;background:linear-gradient(135deg,#2a1f08,#1a1408);display:flex;align-items:center;justify-content:center;font-size:36px;flex-shrink:0;box-shadow:0 0 20px rgba(201,162,39,.2)}}
  .founder-info h3{{font-family:'Noto Serif SC',serif;font-size:24px;color:#f0e0a0;margin-bottom:8px}}
  .founder-info .title-tags{{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:20px}}
  .tag{{background:rgba(201,162,39,.1);border:1px solid rgba(201,162,39,.3);color:#c9a227;font-size:12px;padding:4px 12px;border-radius:2px;letter-spacing:1px}}
  .founder-info p{{font-size:15px;color:#9a8060;line-height:1.9;font-weight:300}}
  .founder-info p strong{{color:#c8b080}}
  .for-who{{padding:64px 60px;background:linear-gradient(180deg,#0a0a0a 0%,#0d0b08 100%);border-bottom:1px solid #1e1810}}
  .section-title{{font-family:'Noto Serif SC',serif;font-size:30px;color:#f0e0a0;text-align:center;margin-bottom:12px}}
  .section-title span{{color:#c9a227}}
  .section-desc{{text-align:center;color:#7a6840;font-size:14px;margin-bottom:48px;letter-spacing:1px}}
  .who-grid{{display:grid;grid-template-columns:1fr 1fr;gap:16px}}
  .who-item{{background:rgba(201,162,39,.04);border:1px solid rgba(201,162,39,.15);padding:24px 28px;border-radius:2px}}
  .who-item .icon{{font-size:24px;margin-bottom:12px}}
  .who-item h4{{font-size:15px;color:#e8d5b0;margin-bottom:8px;font-weight:500}}
  .who-item p{{font-size:13px;color:#7a6840;line-height:1.7}}
  .services{{padding:64px 60px;background:#080808;border-bottom:1px solid #1e1810}}
  .service-item{{display:flex;gap:28px;margin-bottom:48px;padding-bottom:48px;border-bottom:1px solid #1a1408}}
  .service-item:last-child{{margin-bottom:0;padding-bottom:0;border-bottom:none}}
  .service-num{{font-family:'Noto Serif SC',serif;font-size:48px;color:rgba(201,162,39,.15);line-height:1;flex-shrink:0;width:60px;font-weight:900}}
  .service-content h3{{font-family:'Noto Serif SC',serif;font-size:22px;color:#f0e0a0;margin-bottom:8px}}
  .service-content .service-tag{{display:inline-block;background:rgba(201,162,39,.12);color:#c9a227;font-size:11px;padding:3px 10px;margin-bottom:16px;letter-spacing:2px}}
  .service-content p{{font-size:15px;color:#8a7050;line-height:1.9;font-weight:300}}
  .service-content p strong{{color:#c8b080}}
  .pricing{{padding:64px 60px;background:linear-gradient(160deg,#0f0c04 0%,#0a0a0a 100%);border-bottom:1px solid #1e1810;text-align:center}}
  .price-box{{border:1px solid rgba(201,162,39,.4);padding:56px 40px;max-width:480px;margin:0 auto 40px;position:relative;background:rgba(201,162,39,.03)}}
  .price-box::before{{content:'首期限定';position:absolute;top:-1px;right:32px;background:#c9a227;color:#0a0a0a;font-size:11px;letter-spacing:2px;padding:4px 14px;font-weight:700}}
  .price-amount{{font-family:'Noto Serif SC',serif;font-size:72px;font-weight:900;color:#c9a227;line-height:1;margin-bottom:8px}}
  .price-amount .currency{{font-size:32px;vertical-align:top;margin-top:14px;display:inline-block}}
  .price-period{{font-size:14px;color:#6a5830;letter-spacing:2px;margin-bottom:36px}}
  .price-divider{{width:40px;height:1px;background:rgba(201,162,39,.3);margin:0 auto 36px}}
  .price-perks{{list-style:none;text-align:left}}
  .price-perks li{{font-size:15px;color:#9a8060;padding:10px 0;border-bottom:1px solid rgba(201,162,39,.08);display:flex;align-items:center;gap:12px}}
  .price-perks li:last-child{{border-bottom:none}}
  .price-perks li::before{{content:'✦';color:#c9a227;font-size:10px;flex-shrink:0}}
  .quota-note{{font-size:14px;color:#5a4820;letter-spacing:1px}}
  .quota-note strong{{color:#c9a227;font-size:20px;font-family:'Noto Serif SC',serif}}
  .filter{{padding:64px 60px;background:#080808;border-bottom:1px solid #1e1810}}
  .filter-steps{{margin-top:40px}}
  .filter-step{{display:flex;gap:24px;margin-bottom:32px;align-items:flex-start}}
  .step-num{{width:40px;height:40px;border:1px solid #c9a227;display:flex;align-items:center;justify-content:center;font-family:'Noto Serif SC',serif;font-size:16px;color:#c9a227;flex-shrink:0}}
  .step-content h4{{font-size:16px;color:#e8d5b0;margin-bottom:6px;padding-top:8px;font-weight:500}}
  .step-content p{{font-size:13px;color:#6a5830;line-height:1.7}}
  .footer{{padding:56px 60px;background:linear-gradient(180deg,#0a0a0a 0%,#050503 100%);text-align:center}}
  .footer-quote{{font-family:'Noto Serif SC',serif;font-size:20px;color:#8a7050;line-height:1.8;margin-bottom:48px;font-style:italic}}
  .footer-quote strong{{color:#c9a227}}
  .cta-btn{{display:inline-block;border:1px solid #c9a227;color:#0a0a0a;background:#c9a227;font-size:15px;letter-spacing:4px;padding:16px 60px;margin-bottom:24px;font-weight:700}}
  .cta-note{{font-size:12px;color:#4a3818;letter-spacing:1px;margin-bottom:48px}}
  .footer-brand{{font-size:12px;color:#3a2c12;letter-spacing:3px}}
  .footer-brand span{{color:#5a4820}}
</style>
</head>
<body>

<div class="hero">
  <div class="hero-badge">{badge}</div>
  <div class="hero-title">{brand}</div>
  <div class="hero-subtitle">{tagline}</div>
  <div class="hero-divider"></div>
  <div class="hero-intro">{hero_intro}</div>
</div>

<div class="founder">
  <div class="section-label">FOUNDER · 发起人</div>
  <div class="founder-card">
    <div class="founder-avatar">{f_emoji}</div>
    <div class="founder-info">
      <h3>{f_name}</h3>
      <div class="title-tags">{tags_html}</div>
      <p>{f_intro}</p>
    </div>
  </div>
</div>

<div class="for-who">
  <div class="section-label">FOR WHO · 为谁设计</div>
  <div class="section-title">这个圈子，<span>不是为所有人</span>准备的</div>
  <div class="section-desc">以下四种人，你至少占一条</div>
  <div class="who-grid">{who_html}</div>
</div>

<div class="services">
  <div class="section-label">SERVICES · 核心服务</div>
  <div class="section-title" style="margin-bottom:48px">三件事，<span>真正有用</span></div>
  {services_html}
</div>

<div class="pricing">
  <div class="section-label">PRICING · 年费</div>
  <div class="section-title" style="margin-bottom:48px"><span>一个决定</span>，改变一年的质量</div>
  <div class="price-box">
    <div class="price-amount"><span class="currency">{unit}</span>{price}</div>
    <div class="price-period">/ 年 · PER YEAR</div>
    <div class="price-divider"></div>
    <ul class="price-perks">{perks_html}</ul>
  </div>
  <div class="quota-note">首期仅招募 <strong>{quota}</strong> 人 · 名额满即关闭</div>
</div>

<div class="filter">
  <div class="section-label">PROCESS · 入圈流程</div>
  <div class="section-title" style="margin-bottom:8px">不是<span>所有人</span>都能进</div>
  <div class="section-desc">谢导亲自面试，确保每一位成员都值得认识</div>
  <div class="filter-steps">
    <div class="filter-step"><div class="step-num">01</div><div class="step-content"><h4>提交申请</h4><p>填写简单表格：你是谁、你在做什么、你希望从圈子里得到什么</p></div></div>
    <div class="filter-step"><div class="step-num">02</div><div class="step-content"><h4>谢导审阅</h4><p>谢导亲自阅读每一份申请，3 个工作日内给出回复</p></div></div>
    <div class="filter-step"><div class="step-num">03</div><div class="step-content"><h4>一对一面谈</h4><p>30 分钟视频通话，谢导想了解你，也给你机会了解圈子</p></div></div>
    <div class="filter-step"><div class="step-num">04</div><div class="step-content"><h4>正式入圈</h4><p>双向确认后完成年费，正式成为成员，立即享受所有权益</p></div></div>
  </div>
</div>

<div class="footer">
  <div class="footer-quote">{footer_q}</div>
  <div class="cta-btn">申请入圈</div>
  <div class="cta-note">提交申请 · 无需付费 · 双向选择</div>
  <div class="footer-brand">{brand} <span>© {year}</span></div>
</div>

</body>
</html>"""


def screenshot(html_content: str, output_path: str) -> bool:
    with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
        f.write(html_content)
        tmp = f.name
    try:
        r = subprocess.run(
            ["playwright", "screenshot", "--browser", "chromium",
             "--full-page", "--viewport-size", "750,900",
             f"file://{tmp}", output_path],
            capture_output=True, text=True, timeout=60
        )
        if r.returncode != 0:
            print(f"[错误] Playwright: {r.stderr}", file=sys.stderr)
            return False
        print(f"[✅] 图片: {output_path}")
        return True
    finally:
        os.unlink(tmp)


def send_feishu(image_path: str, receive_id: str, app_id: str, app_secret: str,
                receive_id_type: str = "open_id") -> bool:
    import urllib.request
    # token
    req = urllib.request.Request(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        data=json.dumps({"app_id": app_id, "app_secret": app_secret}).encode(),
        headers={"Content-Type": "application/json"}
    )
    with urllib.request.urlopen(req, timeout=15) as r:
        [REDACTED_SECRET])).get("tenant_access_token", "")
    if not [REDACTED_SECRET]"[错误] 获取飞书 token 失败", file=sys.stderr); return False

    # 上传图片
    up = subprocess.run(
        ["curl", "-s", "-X", "POST", "https://open.feishu.cn/open-apis/im/v1/images",
         "-H", f"Authorization: Bearer {token}",
         "-F", "image_type=message", "-F", f"image=@{image_path}"],
        capture_output=True, text=True, timeout=30
    )
    image_key = json.loads(up.stdout).get("data", {}).get("image_key", "")
    if not image_key:
        print(f"[错误] 上传失败: {up.stdout}", file=sys.stderr); return False

    # 发消息
    req2 = urllib.request.Request(
        f"https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type={receive_id_type}",
        data=json.dumps({"receive_id": receive_id, "msg_type": "image",
                         "content": json.dumps({"image_key": image_key})}).encode(),
        headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    )
    with urllib.request.urlopen(req2, timeout=15) as r:
        resp = json.loads(r.read())
    if resp.get("code") == 0:
        print(f"[✅] 飞书发送成功"); return True
    print(f"[错误] 发送失败: {resp}", file=sys.stderr); return False


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--config", help="JSON 配置文件")
    parser.add_argument("--output", default="/tmp/recruit_output.png")
    parser.add_argument("--send-feishu", action="store_true")
    parser.add_argument("--receive-id")
    parser.add_argument("--receive-id-type", default="open_id")
    parser.add_argument("--app-id")
    parser.add_argument("--app-secret")
    args = parser.parse_args()

    config = {}
    if args.config:
        with open(args.config, encoding="utf-8") as f:
            config = json.load(f)

    html = build_html(config)
    if not screenshot(html, args.output):
        sys.exit(1)

    if args.send_feishu:
        send_feishu(args.output, args.receive_id, args.app_id, args.app_secret,
                    args.receive_id_type)


if __name__ == "__main__":
    main()


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