@clawhub-extrastu-5360772b08
生成 Apple 国区 ICP 豁免申请附件 PDF。当用户提到 ICP 备案、Apple 国区下架、ICP 豁免申请、App Store 中国区合规、申请例外批准等相关内容时,立即触发此 skill。收集用户的 Team ID、账户持有人姓名、App ID 等信息,调用脚本生成符合 Apple 要求的正式申请附...
---
name: apple-icp-exemption
description: 生成 Apple 国区 ICP 豁免申请附件 PDF。当用户提到 ICP 备案、Apple 国区下架、ICP 豁免申请、App Store 中国区合规、申请例外批准等相关内容时,立即触发此 skill。收集用户的 Team ID、账户持有人姓名、App ID 等信息,调用脚本生成符合 Apple 要求的正式申请附件 PDF 文件。
---
# Apple 国区 ICP 豁免申请附件生成器
## 概述
此 skill 用于生成 Apple App Store 中国大陆地区 ICP 备案豁免申请所需的正式附件 PDF。
## 触发场景
- 用户提到 ICP 备案/豁免/例外
- 用户的 App 在国区被下架,需要申请豁免
- 用户需要准备 Apple 中国区合规材料
- 用户提到 "申请例外批准"、"ICP 相关申诉" 等
## 信息收集
在生成 PDF 前,需要向用户收集以下信息:
### 必填信息
1. **Team ID**(团队 ID)— 在 App Store Connect → 账户 → 会员资格 中查看
2. **账户持有人法定姓名**(中文全名,与证件一致)
3. **App ID**(应用 ID)— 在 App Store Connect 的 App 详情页中查看
4. **申请日期**(默认今天,用户可更改)
### 信息收集方式
直接在对话中逐一询问,或一次性询问所有信息:
```
请提供以下信息来生成 ICP 豁免申请附件:
1. Team ID(例如:ABCD123456)
2. 账户持有人法定姓名
3. App ID(例如:1234567890)
4. 申请日期(格式:YYYY年MM月DD日,留空则使用今天)
```
## PDF 生成步骤
收集好所有信息后,运行以下命令生成 PDF:
```bash
python3 /home/claude/icp-exemption-skill/scripts/generate_pdf.py \
--team-id "TEAM_ID" \
--name "法定姓名" \
--app-id "APP_ID" \
--date "YYYY年MM月DD日" \
--output "/mnt/user-data/outputs/ICP豁免申请附件.pdf"
```
生成后使用 `present_files` 将 PDF 提供给用户下载。
## 注意事项
- 生成的 PDF 需要用户**手写签名**后再提交
- 一个 App 对应一份附件,多个 App 需分别生成
- 提醒用户核实所有信息与 App Store Connect 账户完全一致
- PDF 使用中文,符合 Apple 中国区审核团队要求
## 申请说明
生成附件后,告知用户提交流程:
1. 打印或在平板上手写签名
2. 扫描/拍照保存为 PDF
3. 登录 App Store Connect,找到被下架的 App
4. 点击「联系我们」→「ICP 相关问题」
5. 上传签名后的附件,说明 App 不联网或仅使用 Apple 服务
6. 提交等待 3-7 个工作日审核
FILE:scripts/generate_pdf.py
#!/usr/bin/env python3
"""
Apple 国区 ICP 豁免申请附件生成器
生成符合 Apple 要求的正式申请附件 PDF
"""
import argparse
import sys
from datetime import datetime
from pathlib import Path
def get_today_chinese():
"""返回今天的中文日期,如 2024年12月01日"""
today = datetime.today()
return f"{today.year}年{today.month:02d}月{today.day:02d}日"
def generate_pdf(team_id: str, name: str, app_id: str, date: str, output_path: str):
try:
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, HRFlowable, Table, TableStyle
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os
except ImportError:
print("错误:缺少 reportlab 库,请运行 pip install reportlab", file=sys.stderr)
sys.exit(1)
# Try to register a CJK font for Chinese characters
font_name = "Helvetica" # fallback
bold_font_name = "Helvetica-Bold"
# Try common CJK font paths on Ubuntu
cjk_fonts = [
("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", "WQYZenHei"),
("/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", "WQYMicroHei"),
("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", "NotoSansCJK"),
("/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", "NotoSansCJK"),
("/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", "NotoSansCJK"),
]
for font_path, font_reg_name in cjk_fonts:
if os.path.exists(font_path):
try:
pdfmetrics.registerFont(TTFont(font_reg_name, font_path))
font_name = font_reg_name
bold_font_name = font_reg_name # use same font for bold too
break
except Exception:
continue
# If no CJK font found, try installing wqy-zenhei
if font_name == "Helvetica":
try:
import subprocess
subprocess.run(
["apt-get", "install", "-y", "-q", "fonts-wqy-zenhei"],
capture_output=True, timeout=60
)
font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"
if os.path.exists(font_path):
pdfmetrics.registerFont(TTFont("WQYZenHei", font_path))
font_name = "WQYZenHei"
bold_font_name = "WQYZenHei"
except Exception:
pass
# Page layout
page_width, page_height = A4
margin = 30 * mm
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
leftMargin=margin,
rightMargin=margin,
topMargin=25 * mm,
bottomMargin=25 * mm,
title="Apple 国区 ICP 豁免申请附件",
author=name,
)
# Styles
def style(name_s, **kwargs):
defaults = dict(fontName=font_name, fontSize=11, leading=18, spaceAfter=0, spaceBefore=0)
defaults.update(kwargs)
return ParagraphStyle(name_s, **defaults)
title_style = style("Title", fontName=bold_font_name, fontSize=16, leading=26,
alignment=TA_CENTER, spaceBefore=0, spaceAfter=8)
subtitle_style = style("Subtitle", fontSize=11, alignment=TA_CENTER, spaceAfter=4)
section_header_style = style("SectionHeader", fontName=bold_font_name, fontSize=12,
leading=20, spaceBefore=14, spaceAfter=4)
body_style = style("Body", fontSize=11, leading=20, alignment=TA_JUSTIFY)
info_key_style = style("InfoKey", fontName=bold_font_name, fontSize=11, leading=20)
info_val_style = style("InfoVal", fontSize=11, leading=20)
declaration_style = style("Declaration", fontSize=11, leading=22, alignment=TA_JUSTIFY)
sign_label_style = style("SignLabel", fontName=bold_font_name, fontSize=11, leading=22)
sign_val_style = style("SignVal", fontSize=11, leading=22)
footer_style = style("Footer", fontSize=9, leading=14, alignment=TA_CENTER,
textColor=colors.grey)
story = []
# ── Title ──────────────────────────────────────────────
story.append(Spacer(1, 6 * mm))
story.append(Paragraph("Apple 国区 ICP 豁免申请附件", title_style))
story.append(Paragraph("App Store Connect 中国大陆地区 ICP 备案例外申请", subtitle_style))
story.append(Spacer(1, 3 * mm))
story.append(HRFlowable(width="100%", thickness=1.5, color=colors.black))
story.append(Spacer(1, 5 * mm))
# ── 账户信息 ──────────────────────────────────────────
story.append(Paragraph("一、账户信息", section_header_style))
info_data = [
[Paragraph("Team ID(团队 ID)", info_key_style),
Paragraph(f":{team_id}", info_val_style)],
[Paragraph("账户持有人法定姓名", info_key_style),
Paragraph(f":{name}", info_val_style)],
]
info_table = Table(info_data, colWidths=[65 * mm, None])
info_table.setStyle(TableStyle([
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, -1), 2),
("BOTTOMPADDING", (0, 0), (-1, -1), 2),
]))
story.append(info_table)
# ── App 信息 ───────────────────────────────────────────
story.append(Paragraph("二、App 信息", section_header_style))
app_data = [
[Paragraph("App ID(应用 ID)", info_key_style),
Paragraph(f":{app_id}", info_val_style)],
]
app_table = Table(app_data, colWidths=[65 * mm, None])
app_table.setStyle(TableStyle([
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 0),
("TOPPADDING", (0, 0), (-1, -1), 2),
("BOTTOMPADDING", (0, 0), (-1, -1), 2),
]))
story.append(app_table)
# ── 声明 ──────────────────────────────────────────────
story.append(Paragraph("三、申请声明", section_header_style))
declarations = [
f"本人 {name},Team ID 为 {team_id},现就 App ID 为 {app_id} 的独立应用,向 Apple 申请中国大陆地区 ICP 备案豁免例外批准。本人声明如下:",
"",
"1. 本人有意就上述独立 App 向 Apple 申请例外批准。",
"",
"2. 本人已充分了解并遵守所有相关法律法规及 Apple 的相关政策要求,确认本 App 属于以下豁免情形之一:",
" • 完全离线应用,不进行任何网络通信;或",
" • 仅通过 iCloud 同步数据,不连接其他任何服务器;或",
" • 仅通过 Apple 内购(IAP)进行交易,无自建支付系统及其他联网功能。",
"",
"3. 本人确认所提交的所有信息真实、准确、完整,与 App Store Connect 账户信息完全一致。",
"",
"4. 如存在任何虚假陈述或误导信息,本人愿意承担由此产生的全部法律责任。",
]
for line in declarations:
if line == "":
story.append(Spacer(1, 3 * mm))
else:
story.append(Paragraph(line, declaration_style))
# ── 签署 ──────────────────────────────────────────────
story.append(Spacer(1, 8 * mm))
story.append(HRFlowable(width="100%", thickness=0.8, color=colors.HexColor("#aaaaaa")))
story.append(Spacer(1, 5 * mm))
story.append(Paragraph("四、签署", section_header_style))
story.append(Spacer(1, 2 * mm))
# Signature area as a table
sig_line = "_" * 20
sign_data = [
[Paragraph("手写签名:", sign_label_style),
Paragraph(sig_line, sign_val_style),
Paragraph("", sign_val_style)],
[Paragraph("正楷姓名:", sign_label_style),
Paragraph(name, sign_val_style),
Paragraph("", sign_val_style)],
[Paragraph("日 期:", sign_label_style),
Paragraph(date, sign_val_style),
Paragraph("", sign_val_style)],
]
sign_table = Table(sign_data, colWidths=[30 * mm, 80 * mm, None])
sign_table.setStyle(TableStyle([
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("LEFTPADDING", (0, 0), (-1, -1), 0),
("RIGHTPADDING", (0, 0), (-1, -1), 4),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
]))
story.append(sign_table)
story.append(Spacer(1, 10 * mm))
# ── 注意事项 ─────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.8, color=colors.HexColor("#aaaaaa")))
story.append(Spacer(1, 4 * mm))
notice_style = style("Notice", fontSize=9, leading=15, textColor=colors.HexColor("#555555"),
alignment=TA_JUSTIFY)
story.append(Paragraph(
"【注意事项】本附件仅供 Apple App Store Connect ICP 豁免申请使用。"
"请在手写签名后将本文件扫描或拍照,作为附件上传至 App Store Connect 申诉流程中。"
"如有多个 App 需要申请,请为每个 App 单独准备并提交一份附件。",
notice_style
))
story.append(Spacer(1, 5 * mm))
story.append(Paragraph(
f"本文件由 Apple ICP 豁免申请助手自动生成 · 生成日期:{date}",
footer_style
))
doc.build(story)
print(f"PDF 生成成功:{output_path}")
def main():
parser = argparse.ArgumentParser(description="生成 Apple 国区 ICP 豁免申请附件 PDF")
parser.add_argument("--team-id", required=True, help="Team ID(团队 ID)")
parser.add_argument("--name", required=True, help="账户持有人法定姓名")
parser.add_argument("--app-id", required=True, help="App ID")
parser.add_argument("--date", default="", help="申请日期(留空则使用今天)")
parser.add_argument("--output", default="/mnt/user-data/outputs/ICP豁免申请附件.pdf",
help="输出路径")
args = parser.parse_args()
date = args.date if args.date else get_today_chinese()
Path(args.output).parent.mkdir(parents=True, exist_ok=True)
generate_pdf(args.team_id, args.name, args.app_id, date, args.output)
if __name__ == "__main__":
main()
Extract and clean readable article content, metadata, and markdown from URLs or HTML for research, note taking, and web scraping.
---
name: defuddle-web-cleaner
description: extract clean article content from web pages using defuddle. use when a user provides a url or html and wants the readable article text, markdown version, or structured metadata. helpful for web scraping, research workflows, note taking, obsidian clipping, and converting web pages to markdown.
---
# Defuddle Web Cleaner
Extract the main readable content from a web page.
This skill removes unnecessary elements such as:
- navigation bars
- sidebars
- ads
- comments
- footers
- social buttons
The result is clean article content.
## Supported Inputs
1. URL
2. Raw HTML
3. Web page text
## Output Format
Default output:
Title
Author
Site
Published date
Markdown article content
Alternative output (JSON):
{
title,
author,
site,
description,
published,
content,
contentMarkdown
}
## Processing Steps
1. Detect input type
2. Load page HTML
3. Run Defuddle parser
4. Extract metadata
5. Convert to Markdown if requested
6. Return clean content
## Example
Input:
https://example.com/blog/ai
Output:
Title: AI is Changing Everything
Author: Jane Smith
Site: Example Blog
Markdown:
# AI is Changing Everything
Artificial intelligence is transforming industries...
## Tips
Use this skill when:
- saving articles to Obsidian
- building research datasets
- cleaning webpages for LLM processing
- summarizing articles
FILE:agents/openai.yaml
interface:
display_name: Defuddle Web Cleaner
short_description: Convert web pages into clean markdown articles
category: productivity
FILE:references/defuddle-usage.md
# Defuddle Reference
## Basic usage
URL conversion:
curl defuddle.md/example.com/article
Returns markdown with YAML frontmatter.
## CLI
defuddle parse https://example.com/article --markdown
Options:
--markdown
--json
--property
--output
## Returned Metadata
- title
- author
- description
- site
- domain
- favicon
- image
- published
- wordCount
- parseTime