diff --git a/02_卡人(水)/水桥_平台对接/接收短信/SKILL.md b/02_卡人(水)/水桥_平台对接/接收短信/SKILL.md new file mode 100644 index 00000000..07640fde --- /dev/null +++ b/02_卡人(水)/水桥_平台对接/接收短信/SKILL.md @@ -0,0 +1,104 @@ +--- +name: 接收短信 +description: 通过 receivesms 类网站获取临时号码并抓取该号码最新一条短信(含发件人名字与内容)。触发词:接收短信、收短信、receivesms、接码、临时号码、获取短信、拿短信。 +owner: 水桥 +group: 水 +version: "1.0" +updated: "2026-03-01" +--- + +# 接收短信 Skill + +> 从接码网站取号、拿最新短信,命令行完成,不打开网页。 —— 水桥 + +--- + +## 一、负责与入口 + +- **负责人**:水桥(平台对接) +- **触发词**:接收短信、收短信、receivesms、接码、临时号码、获取短信、拿短信、等刷新拿短信 +- **数据源网站**:**receivesms.co**(英国临时号码列表与收件页,公开、免注册) + +--- + +## 二、要获取的「网站短信」类型说明 + +本技能最终输出两类信息,请按需使用: + +| 输出项 | 含义 | 示例 | +|:---|:---|:---| +| **号码** | 当前使用的临时号码(来自 receivesms.co 英国号列表) | +447424907088 | +| **短信内容** | 该号码收件页上**最新一条**短信的正文 | `[PUBG] code: 697881. Valid for 3 minutes.` | +| **发件人名字(网站/服务名)** | 页面上显示的发送方标识,即「来自哪个网站/服务的短信」 | TRIBBU、bilibili、AIRBNB、hcloud、WhatsApp 等 | + +**当前脚本行为**:只输出**号码 + 最新一条短信正文**;发件人名字在网页上对应 `class="from-link"`,若你需要「只要某类网站/服务发来的短信」(如只要 bilibili、只要验证码类),可在本 Skill 下扩展脚本按发件人或关键词过滤。 + +**可获取的短信类型(按来源名)**:凡在 receivesms.co 该号码收件页上出现的都会被抓到「最新一条」—— 包括但不限于:验证码类(各 App/网站 OTP)、营销类、通知类;发件人显示为服务名/短号(如 TRIBBU、hcloud、+***5113)。若你要的是「自己刚发过去的那条」,请用 `--wait` 模式(见下)。 + +--- + +## 三、整体流程(从取号到拿到短信) + +``` +① 请求 receivesms.co 英国号码列表页 + ↓ +② 随机选取一个临时号码(+44 开头) + ↓ +③ [可选] --wait:先输出号码,等待 30~60 秒(你向该号发短信),再请求该号收件页 + 或无 --wait:直接请求该号收件页 + ↓ +④ 解析收件页 HTML,取「最新一条」短信正文(
) + ↓ +⑤ 输出:NUMBER、SMS(及 号码 | 短信 一行) +``` + +--- + +## 四、执行方式(命令行,不打开网页) + +**脚本路径**(工作台内): + +``` +运营中枢/scripts/receivesms_get_sms.py +``` + +**用法**: + +| 模式 | 命令 | 说明 | +|:---|:---|:---| +| 立即取最新一条 | `python3 receivesms_get_sms.py` | 可能拿到历史/限流旧短信 | +| 等刷新后取最新 | `python3 receivesms_get_sms.py --wait` | 先出号,等 45 秒后再抓,适合「你发短信后」拿刚收到的那条 | + +**执行目录**: + +```bash +cd /Users/karuo/Documents/个人/卡若AI/运营中枢/scripts +python3 receivesms_get_sms.py +# 或 +python3 receivesms_get_sms.py --wait +``` + +--- + +## 五、输出格式 + +脚本标准输出示例: + +``` +NUMBER: +447476933927 +SMS: [PUBG] code: 697881. Valid for 3 minutes. +--- ++447476933927 | [PUBG] code: 697881. Valid for 3 minutes. +``` + +- **NUMBER**:当前使用的临时号码(receivesms.co 英国号)。 +- **SMS**:该号码在 receivesms.co 收件页上的**最新一条短信正文**;无短信时为 `(无)`。 +- 最后一行:`号码 | 短信`,便于复制或管道处理。 + +--- + +## 六、参考资料与扩展 + +- **流程史记**:`运营中枢/参考资料/giffgaff发短信收短信_流程史记.md`(giffgaff 发短信到临时号时的操作与保号)。 +- **接码操作说明**:`运营中枢/参考资料/receivesms收短信_操作.md`(命令行用法摘要)。 +- **扩展**:若需按「发件人名字」或关键词过滤短信,可在本目录下扩展脚本(解析 `from-link` 或短信内容),并在本 SKILL 更新「要获取的网站短信类型」说明。 diff --git a/SKILL_REGISTRY.md b/SKILL_REGISTRY.md index 8d05b903..0c589034 100644 --- a/SKILL_REGISTRY.md +++ b/SKILL_REGISTRY.md @@ -1,8 +1,8 @@ # 卡若AI 技能注册表(Skill Registry) > **一张表查所有技能**。任何 AI 拿到这张表,就能按关键词找到对应技能的 SKILL.md 路径并执行。 -> 63 技能 | 14 成员 | 5 负责人 -> 版本:5.3 | 更新:2026-02-26 +> 64 技能 | 14 成员 | 5 负责人 +> 版本:5.4 | 更新:2026-03-01 --- @@ -89,6 +89,7 @@ | W12 | MCP 搜索与连接 | 水桥 | **MCP、找MCP、连接MCP、MCP搜索、发现MCP、添加MCP、需要MCP、MCP安装、MCP发现、查MCP、装MCP** | `02_卡人(水)/水桥_平台对接/MCP管理/SKILL.md` | 搜索 5000+ MCP 服务器→生成安装配置→写入 Cursor/Claude 等 | | W13 | Excel表格与日报 | 水桥 | **Excel写飞书、Excel导入飞书、批量写飞书表格、飞书表格导入、CSV写飞书、日报图表发飞书、表格日报** | `02_卡人(水)/水桥_平台对接/飞书管理/Excel表格与日报_SKILL.md` | 本地 Excel/CSV→飞书表格→自动日报图表→发飞书群 | | W14 | **卡猫复盘** | 水桥 | **卡猫复盘、婼瑄复盘、卡猫今日复盘、婼瑄今日、复盘到卡猫、发卡猫群** | `02_卡人(水)/水桥_平台对接/飞书管理/卡猫复盘/SKILL.md` | 婼瑄目录→目标=今年总目标+完成%+人/事/数具体→飞书+卡猫群 | +| W15 | **接收短信** | 水桥 | **接收短信、收短信、receivesms、接码、临时号码、获取短信、拿短信、等刷新拿短信** | `02_卡人(水)/水桥_平台对接/接收短信/SKILL.md` | receivesms.co 取英国临时号→命令行抓该号最新一条短信(可 --wait 等刷新);输出号码+短信,含「要获取的网站短信类型」说明 | ## 木组 · 卡木(产品内容创造) diff --git a/运营中枢/scripts/receivesms_analyze_all.py b/运营中枢/scripts/receivesms_analyze_all.py new file mode 100644 index 00000000..e3e2e62a --- /dev/null +++ b/运营中枢/scripts/receivesms_analyze_all.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +receivesms.co 整站收短信内容分析:拉取英国号列表下所有号码的收件页,汇总全部短信, +按发件人、类型、关键词做统计并输出分析报告。 +""" +import re +import time +import urllib.request +from collections import Counter, defaultdict + +BASE = "https://www.receivesms.co" +LIST_URL = BASE + "/british-phone-numbers/gb/" + + +def fetch(url): + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"}) + with urllib.request.urlopen(req, timeout=20) as r: + return r.read().decode("utf-8", errors="replace") + + +def parse_number_list(html): + blocks = re.findall( + r'href="/gb-phone-number/(\d+)/"[^>]*>.*?(\+44[\d\s]+)', + html, + re.DOTALL + ) + return [(mid, num.replace(" ", "").strip()) for mid, num in blocks] + + +def parse_all_sms_in_inbox(html): + """解析一页收件页中所有短信:发件人 + 正文。""" + # 按 entry 块切:from-link 与紧随其后的 entry-body > div.sms + pattern = r'([^<]*)\s*
(.*?)
' + matches = re.findall(pattern, html, re.DOTALL) + out = [] + for sender, raw in matches: + text = re.sub(r"<[^>]+>", "", raw) + text = text.replace(" ", " ").replace("&", "&").replace("<", "<").replace(">", ">").replace("'", "'") + text = " ".join(text.split()).strip() + if text: + out.append((sender.strip(), text)) + return out + + +def classify_content(text): + """简单分类:验证码/OTP、营销/推广、通知、其他。""" + t = text.upper() + if any(k in t for k in ["CODE", "OTP", "VERIFICATION", "验证码", "验证", "CODIGO", "KODU"]): + return "验证码/OTP" + if any(k in t for k in ["PROMO", "OFFER", "DEAL", "SALE", "优惠", "促销", "http", "LINK", ".COM"]): + return "营销/推广" + if any(k in t for k in ["LOGIN", "CONFIRM", "ALERT", "NOTICE", "通知", "确认"]): + return "通知" + return "其他" + + +def main(): + print("正在获取英国号码列表…", flush=True) + html_list = fetch(LIST_URL) + pairs = parse_number_list(html_list) + if not pairs: + print("未解析到任何号码") + return + print(f"共 {len(pairs)} 个号码,开始逐页拉取收件…", flush=True) + all_messages = [] # (number, sender, text) + for i, (mid, number) in enumerate(pairs): + url = f"{BASE}/gb-phone-number/{mid}/" + try: + html = fetch(url) + msgs = parse_all_sms_in_inbox(html) + for sender, text in msgs: + all_messages.append((number, sender, text)) + print(f" [{i+1}/{len(pairs)}] {number}: {len(msgs)} 条", flush=True) + except Exception as e: + print(f" [{i+1}/{len(pairs)}] {number}: 失败 {e}", flush=True) + time.sleep(0.8) + if not all_messages: + print("未采集到任何短信") + return + # 统计 + by_sender = Counter(s for _, s, _ in all_messages) + by_type = Counter(classify_content(t) for _, _, t in all_messages) + keywords = [] + for _, _, t in all_messages: + for w in re.findall(r"[a-zA-Z]{3,}", t): + keywords.append(w.lower()) + kw_top = Counter(keywords).most_common(25) + # 输出报告(Markdown) + lines = [ + "# receivesms.co 整站收短信内容分析报告", + "", + "> 数据来源:receivesms.co 英国临时号码列表下全部号码的收件页;采集时间:一次运行。", + "", + "## 一、概览", + "", + f"- **号码数量**:{len(pairs)}", + f"- **短信总条数**:{len(all_messages)}", + f"- **去重发件人数量**:{len(by_sender)}", + "", + "## 二、按发件人(网站/服务名)统计", + "", + "| 发件人 | 条数 |", + "|:---|:---|", + ] + for sender, cnt in by_sender.most_common(40): + lines.append(f"| {sender} | {cnt} |") + lines.extend([ + "", + "## 三、按内容类型分类", + "", + "| 类型 | 条数 | 占比 |", + "|:---|:---|:---|", + ]) + for typ, cnt in by_type.most_common(): + pct = round(100 * cnt / len(all_messages), 1) + lines.append(f"| {typ} | {cnt} | {pct}% |") + lines.extend([ + "", + "## 四、正文高频词(英文,≥3 字母)", + "", + "| 词 | 出现次数 |", + "|:---|:---|", + ]) + for w, c in kw_top: + lines.append(f"| {w} | {c} |") + lines.extend([ + "", + "## 五、样例短信(每类各 2 条)", + "", + ]) + by_type_list = defaultdict(list) + for num, sender, text in all_messages: + typ = classify_content(text) + by_type_list[typ].append((sender, text[:120])) + for typ in ["验证码/OTP", "营销/推广", "通知", "其他"]: + lines.append(f"### {typ}") + lines.append("") + for sender, snippet in by_type_list.get(typ, [])[:2]: + lines.append(f"- **{sender}**:{snippet}…") + lines.append("") + report = "\n".join(lines) + print("\n" + "=" * 60) + print(report) + # 写文件 + out_path = "/Users/karuo/Documents/卡若Ai的文件夹/报告/receivesms_整站收短信内容分析.md" + import os + os.makedirs(os.path.dirname(out_path), exist_ok=True) + with open(out_path, "w", encoding="utf-8") as f: + f.write(report) + print("=" * 60) + print(f"报告已写入:{out_path}") + + +if __name__ == "__main__": + main() diff --git a/运营中枢/scripts/receivesms_get_sms.py b/运营中枢/scripts/receivesms_get_sms.py new file mode 100644 index 00000000..88d3e579 --- /dev/null +++ b/运营中枢/scripts/receivesms_get_sms.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +receivesms.co 命令行:随机取一个英国临时号码,抓取该号码最新一条短信并输出。 +用法: + python3 receivesms_get_sms.py # 立即取最新一条(可能是历史/限流旧数据) + python3 receivesms_get_sms.py --wait # 取号 → 等 45 秒 → 再刷新收件页取最新(拿到你刚发的) +""" +import re +import random +import sys +import time +import urllib.request + +BASE = "https://www.receivesms.co" +LIST_URL = BASE + "/british-phone-numbers/gb/" + + +def fetch(url): + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"}) + with urllib.request.urlopen(req, timeout=15) as r: + return r.read().decode("utf-8", errors="replace") + + +def parse_number_list(html): + # 每个 card 块内: href="/gb-phone-number/ID/" 与 +44 xxx + blocks = re.findall( + r'href="/gb-phone-number/(\d+)/"[^>]*>.*?(\+44[\d\s]+)', + html, + re.DOTALL + ) + pairs = [(mid, num.replace(" ", "").strip()) for mid, num in blocks] + return pairs + + +def parse_latest_sms(html): + # 第一条
...
即最新短信 + m = re.search(r'
(.*?)
', html, re.DOTALL) + if not m: + return None + text = m.group(1).strip() + # 去 HTML 实体与多余空白 + text = re.sub(r"<[^>]+>", "", text) + text = text.replace(" ", " ").replace("&", "&").replace("<", "<").replace(">", ">").replace(""", '"') + return " ".join(text.split()) + + +def main(): + do_wait = "--wait" in sys.argv + try: + html_list = fetch(LIST_URL) + except Exception as e: + print("ERROR: 获取号码列表失败:", e, file=sys.stderr) + sys.exit(1) + pairs = parse_number_list(html_list) + if not pairs: + print("ERROR: 未解析到任何号码", file=sys.stderr) + sys.exit(1) + mid, number = random.choice(pairs) + inbox_url = f"{BASE}/gb-phone-number/{mid}/" + print("NUMBER:", number) + if do_wait: + wait_sec = 45 # 30~60 秒取中值,等刷新后的新短信 + print(f"请向该号码发短信,{wait_sec} 秒后自动刷新收件页…", flush=True) + time.sleep(wait_sec) + try: + html_inbox = fetch(inbox_url) + except Exception as e: + print("ERROR: 获取收件页失败:", e, file=sys.stderr) + sys.exit(1) + sms = parse_latest_sms(html_inbox) + print("SMS:", sms if sms else "(无)") + if sms: + print("---") + print(number, "|", sms) + + +if __name__ == "__main__": + main() diff --git a/运营中枢/scripts/sora2_generate.py b/运营中枢/scripts/sora2_generate.py new file mode 100644 index 00000000..8bcf8f25 --- /dev/null +++ b/运营中枢/scripts/sora2_generate.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Sora 2 视频生成 API 脚本 +- 创建任务、轮询状态、下载 MP4,一键生成 +- 需环境变量 OPENAI_API_KEY;输出目录:卡若Ai的文件夹/导出/ +""" +from __future__ import annotations + +import os +import sys +import time +import argparse +from pathlib import Path + +try: + import requests +except ImportError: + print("请先安装: pip install requests") + sys.exit(1) + +# 默认输出目录(卡若AI 输出规范) +OUTPUT_BASE = Path("/Users/karuo/Documents/卡若Ai的文件夹/导出") +BASE_URL = "https://api.openai.com/v1/videos" +POLL_INTERVAL = 15 +MAX_POLL_MINUTES = 20 + + +def get_api_key() -> str: + key = os.environ.get("OPENAI_API_KEY", "").strip() + if not key: + raise SystemExit("请设置环境变量 OPENAI_API_KEY") + return key + + +def create_video( + api_key: str, + prompt: str, + model: str = "sora-2", + size: str = "1280x720", + seconds: str = "8", + input_reference_path: str | None = None, +) -> dict: + """创建视频生成任务,返回 job 对象(含 id、status)。""" + headers = {"Authorization": f"Bearer {api_key}"} + data = { + "prompt": prompt, + "model": model, + "size": size, + "seconds": seconds, + } + files = None + if input_reference_path and os.path.isfile(input_reference_path): + mime = "image/jpeg" + if input_reference_path.lower().endswith(".png"): + mime = "image/png" + elif input_reference_path.lower().endswith(".webp"): + mime = "image/webp" + files = {"input_reference": (os.path.basename(input_reference_path), open(input_reference_path, "rb"), mime)} + + if files: + resp = requests.post(BASE_URL, headers=headers, data=data, files=files, timeout=60) + for f in files.values(): + f[1].close() + else: + resp = requests.post(BASE_URL, headers=headers, data=data, timeout=60) + + resp.raise_for_status() + return resp.json() + + +def get_video_status(api_key: str, video_id: str) -> dict: + """查询视频任务状态。""" + url = f"{BASE_URL}/{video_id}" + resp = requests.get(url, headers={"Authorization": f"Bearer {api_key}"}, timeout=30) + resp.raise_for_status() + return resp.json() + + +def download_video(api_key: str, video_id: str, save_path: Path, variant: str = "video") -> Path: + """下载视频/缩略图/雪碧图到 save_path。variant: video | thumbnail | spritesheet""" + url = f"{BASE_URL}/{video_id}/content" + if variant != "video": + url += f"?variant={variant}" + resp = requests.get(url, headers={"Authorization": f"Bearer {api_key}"}, stream=True, timeout=120) + resp.raise_for_status() + save_path.parent.mkdir(parents=True, exist_ok=True) + with open(save_path, "wb") as f: + for chunk in resp.iter_content(chunk_size=8192): + f.write(chunk) + return save_path + + +def create_and_download( + prompt: str, + model: str = "sora-2", + size: str = "1280x720", + seconds: str = "8", + input_reference: str | None = None, + output_dir: Path | None = None, + poll_interval: int = POLL_INTERVAL, + max_wait_minutes: int = MAX_POLL_MINUTES, +) -> Path: + """ + 创建 Sora 2 视频任务,轮询直到完成,下载 MP4 到 output_dir。 + 返回最终 MP4 的 Path。 + """ + api_key = get_api_key() + out = output_dir or OUTPUT_BASE + out.mkdir(parents=True, exist_ok=True) + + job = create_video(api_key, prompt, model=model, size=size, seconds=seconds, input_reference_path=input_reference) + video_id = job.get("id") + if not video_id: + raise RuntimeError(f"创建任务失败,无 id: {job}") + + print(f"任务已创建: {video_id},轮询中...") + deadline = time.time() + max_wait_minutes * 60 + while time.time() < deadline: + status_obj = get_video_status(api_key, video_id) + status = status_obj.get("status", "") + progress = status_obj.get("progress", 0) + if status == "completed": + break + if status == "failed": + err = status_obj.get("error", {}) or {} + raise RuntimeError(f"生成失败: {err.get('message', status_obj)}") + print(f" 状态: {status}, 进度: {progress}%") + time.sleep(poll_interval) + + if status != "completed": + raise RuntimeError("超时未完成,请稍后用 video_id 自行下载") + + # 下载 MP4,文件名含 video_id 前 12 位避免重复 + short_id = video_id.replace("video_", "")[:12] + save_path = out / f"sora2_{short_id}.mp4" + download_video(api_key, video_id, save_path, variant="video") + print(f"已保存: {save_path}") + return save_path + + +def main(): + parser = argparse.ArgumentParser(description="Sora 2 视频生成:创建任务并下载 MP4") + parser.add_argument("prompt", nargs="?", help="视频描述文案(也可用 -p)") + parser.add_argument("-p", "--prompt", dest="prompt_opt", help="视频描述文案") + parser.add_argument("-m", "--model", default="sora-2", choices=["sora-2", "sora-2-pro"], help="模型") + parser.add_argument("-s", "--size", default="1280x720", + choices=["720x1280", "1280x720", "1024x1792", "1792x1024"], help="分辨率") + parser.add_argument("--seconds", default="8", choices=["4", "8", "12"], help="时长(秒)") + parser.add_argument("-i", "--input-reference", help="首帧参考图路径(可选)") + parser.add_argument("-o", "--output-dir", type=Path, default=OUTPUT_BASE, help="MP4 输出目录") + parser.add_argument("--poll-interval", type=int, default=POLL_INTERVAL, help="轮询间隔(秒)") + parser.add_argument("--max-wait", type=int, default=MAX_POLL_MINUTES, help="最长等待(分钟)") + args = parser.parse_args() + + prompt = args.prompt_opt or args.prompt + if not prompt: + parser.error("请提供 prompt(位置参数或 -p/--prompt)") + + create_and_download( + prompt=prompt, + model=args.model, + size=args.size, + seconds=args.seconds, + input_reference=args.input_reference, + output_dir=args.output_dir, + poll_interval=args.poll_interval, + max_wait_minutes=args.max_wait, + ) + + +if __name__ == "__main__": + main() diff --git a/运营中枢/参考资料/giffgaff发短信收短信_流程史记.md b/运营中枢/参考资料/giffgaff发短信收短信_流程史记.md index 3f6c040d..3301eddc 100644 --- a/运营中枢/参考资料/giffgaff发短信收短信_流程史记.md +++ b/运营中枢/参考资料/giffgaff发短信收短信_流程史记.md @@ -5,6 +5,16 @@ --- +## 精简版(约 300 字) + +**需要什么**:giffgaff SIM(或 eSIM)、插该卡的手机、账户有余额(Pay as you go 需先充值)。 + +**如何操作**:① 插卡后约 30 分钟内收欢迎短信即激活。② 发短信:手机「信息」App,用 giffgaff 号;英国 +44 7XXX…,中国 +86…。③ 收短信:同一「信息」里看。④ 查余额:giffgaff.com → Log in,手机号+短信验证码(官网不能发/看短信)。⑤ 发不出:设置→蜂窝→运营商选移动/联通;eSIM 或换机后短信中心号改为 +447802002606。 + +**费用**:发短信约 **0.3 英镑/条**;收短信一般不扣费。保号:**180 天**内余额须有变动(发 1 条短信或充值),否则号可能被收;建议每 5~6 个月发一条到 +447973000186。关语音信箱防扣费:拨号盘输入 ##002#。 + +--- + ## 一、总览 | 动作 | 入口 | 说明 | diff --git a/运营中枢/参考资料/receivesms收短信_操作.md b/运营中枢/参考资料/receivesms收短信_操作.md new file mode 100644 index 00000000..514042fd --- /dev/null +++ b/运营中枢/参考资料/receivesms收短信_操作.md @@ -0,0 +1,15 @@ +# receivesms 收短信 · 命令行用法 + +## 一键取号 + 取最新短信(不打开网页) + +```bash +cd /Users/karuo/Documents/个人/卡若AI/运营中枢/scripts +python3 receivesms_get_sms.py +``` + +**输出示例**: +- `NUMBER: +44xxxxxxxxx`:随机到的英国临时号码 +- `SMS: ...`:该号码当前最新一条短信内容(无则显示 `(无)`) +- 最后一行:`号码 | 短信` 便于复制 + +数据来源:https://www.receivesms.co 英国号列表与对应收件页,纯命令行抓取。 diff --git a/运营中枢/参考资料/sora2_api_使用说明.md b/运营中枢/参考资料/sora2_api_使用说明.md new file mode 100644 index 00000000..c3a5a191 --- /dev/null +++ b/运营中枢/参考资料/sora2_api_使用说明.md @@ -0,0 +1,71 @@ +# Sora 2 生成 API 使用说明 + +> 脚本位置:`运营中枢/scripts/sora2_generate.py` +> 输出目录:`/Users/karuo/Documents/卡若Ai的文件夹/导出/` + +## 前置条件 + +1. **API Key**:需 OpenAI 已开通 Sora 2 权限的账号,在环境变量中设置: + ```bash + export OPENAI_API_KEY="sk-xxx" + ``` +2. **权限说明**:Sora 2 API 目前为预览,需在 [OpenAI 控制台](https://platform.openai.com/) 确认账号已开通 Video API;未开通需联系 OpenAI 申请。 + +## 命令行用法 + +```bash +# 进入工作台 +cd /Users/karuo/Documents/个人/卡若AI + +# 仅文本生成(必填 prompt) +python 运营中枢/scripts/sora2_generate.py "一只橘猫在窗台上打哈欠,阳光洒进来" + +# 指定模型、分辨率、时长 +python 运营中枢/scripts/sora2_generate.py "海边日落延时" -m sora-2-pro -s 1792x1024 --seconds 12 + +# 首帧参考图(图生视频) +python 运营中枢/scripts/sora2_generate.py "她转身微笑,慢慢走出画面" -i /path/to/sample_720p.jpeg +``` + +### 参数一览 + +| 参数 | 简写 | 默认 | 说明 | +|:---|:---|:---|:---| +| prompt | -p | 必填 | 视频描述文案 | +| model | -m | sora-2 | sora-2 / sora-2-pro | +| size | -s | 1280x720 | 720x1280 / 1280x720 / 1024x1792 / 1792x1024 | +| seconds | — | 8 | 4 / 8 / 12 | +| input-reference | -i | — | 首帧参考图路径(可选) | +| output-dir | -o | 卡若Ai的文件夹/导出 | MP4 保存目录 | +| poll-interval | — | 15 | 轮询间隔(秒) | +| max-wait | — | 20 | 最长等待(分钟) | + +## 在代码中调用 + +```python +from pathlib import Path +import sys +sys.path.insert(0, "/Users/karuo/Documents/个人/卡若AI/运营中枢/scripts") +from sora2_generate import create_and_download, get_api_key, create_video, get_video_status, download_video + +# 一键生成并下载 +out_path = create_and_download( + prompt="一只橘猫在窗台上打哈欠", + model="sora-2", + size="1280x720", + seconds="8", + output_dir=Path("/Users/karuo/Documents/卡若Ai的文件夹/导出"), +) +# out_path 为最终 MP4 路径 +``` + +## API 端点摘要(直连用) + +- **创建任务**:`POST https://api.openai.com/v1/videos` + - Content-Type: multipart/form-data + - 字段:prompt(必填), model, size, seconds, input_reference(文件可选) +- **查询状态**:`GET https://api.openai.com/v1/videos/{video_id}` +- **下载视频**:`GET https://api.openai.com/v1/videos/{video_id}/content` + - 可选 `?variant=thumbnail` 或 `?variant=spritesheet` + +定价(参考):sora-2 约 $0.10/秒,sora-2-pro 约 $0.30–0.50/秒(按分辨率)。 diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 70525dda..58e36599 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -171,3 +171,4 @@ | 2026-02-28 06:25:45 | 🔄 卡若AI 同步 2026-02-28 06:25 | 更新:水桥平台对接、卡木、火炬、运营中枢工作台 | 排除 >20MB: 14 个 | | 2026-02-28 13:25:18 | 🔄 卡若AI 同步 2026-02-28 13:25 | 更新:Cursor规则、金仓、火炬、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 14 个 | | 2026-02-28 13:27:29 | 🔄 卡若AI 同步 2026-02-28 13:27 | 更新:火炬、运营中枢工作台 | 排除 >20MB: 14 个 | +| 2026-03-01 06:14:25 | 🔄 卡若AI 同步 2026-03-01 06:14 | 更新:金盾、水桥平台对接、水溪整理归档、卡木、总索引与入口、运营中枢参考资料、运营中枢工作台、运营中枢技能路由 | 排除 >20MB: 14 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index 855e339e..1128a259 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -174,3 +174,4 @@ | 2026-02-28 06:25:45 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-28 06:25 | 更新:水桥平台对接、卡木、火炬、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-28 13:25:18 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-28 13:25 | 更新:Cursor规则、金仓、火炬、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-28 13:27:29 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-28 13:27 | 更新:火炬、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-03-01 06:14:25 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-01 06:14 | 更新:金盾、水桥平台对接、水溪整理归档、卡木、总索引与入口、运营中枢参考资料、运营中枢工作台、运营中枢技能路由 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |