🔄 卡若AI 同步 2026-03-01 06:54 | 更新:水桥平台对接、总索引与入口、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 14 个
This commit is contained in:
104
02_卡人(水)/水桥_平台对接/接收短信/SKILL.md
Normal file
104
02_卡人(水)/水桥_平台对接/接收短信/SKILL.md
Normal file
@@ -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,取「最新一条」短信正文(<div class="sms">)
|
||||
↓
|
||||
⑤ 输出: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 更新「要获取的网站短信类型」说明。
|
||||
@@ -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 等刷新);输出号码+短信,含「要获取的网站短信类型」说明 |
|
||||
|
||||
## 木组 · 卡木(产品内容创造)
|
||||
|
||||
|
||||
156
运营中枢/scripts/receivesms_analyze_all.py
Normal file
156
运营中枢/scripts/receivesms_analyze_all.py
Normal file
@@ -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+)/"[^>]*>.*?<strong>(\+44[\d\s]+)</strong>',
|
||||
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'<a href="[^"]*" class="from-link">([^<]*)</a>\s*<div class="entry-body"><div class="sms">(.*?)</div></div>'
|
||||
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()
|
||||
79
运营中枢/scripts/receivesms_get_sms.py
Normal file
79
运营中枢/scripts/receivesms_get_sms.py
Normal file
@@ -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/" 与 <strong>+44 xxx</strong>
|
||||
blocks = re.findall(
|
||||
r'href="/gb-phone-number/(\d+)/"[^>]*>.*?<strong>(\+44[\d\s]+)</strong>',
|
||||
html,
|
||||
re.DOTALL
|
||||
)
|
||||
pairs = [(mid, num.replace(" ", "").strip()) for mid, num in blocks]
|
||||
return pairs
|
||||
|
||||
|
||||
def parse_latest_sms(html):
|
||||
# 第一条 <div class="sms">...</div> 即最新短信
|
||||
m = re.search(r'<div class="sms">(.*?)</div>', 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()
|
||||
173
运营中枢/scripts/sora2_generate.py
Normal file
173
运营中枢/scripts/sora2_generate.py
Normal file
@@ -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()
|
||||
@@ -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#。
|
||||
|
||||
---
|
||||
|
||||
## 一、总览
|
||||
|
||||
| 动作 | 入口 | 说明 |
|
||||
|
||||
15
运营中枢/参考资料/receivesms收短信_操作.md
Normal file
15
运营中枢/参考资料/receivesms收短信_操作.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# receivesms 收短信 · 命令行用法
|
||||
|
||||
## 一键取号 + 取最新短信(不打开网页)
|
||||
|
||||
```bash
|
||||
cd /Users/karuo/Documents/个人/卡若AI/运营中枢/scripts
|
||||
python3 receivesms_get_sms.py
|
||||
```
|
||||
|
||||
**输出示例**:
|
||||
- `NUMBER: +44xxxxxxxxx`:随机到的英国临时号码
|
||||
- `SMS: ...`:该号码当前最新一条短信内容(无则显示 `(无)`)
|
||||
- 最后一行:`号码 | 短信` 便于复制
|
||||
|
||||
数据来源:https://www.receivesms.co 英国号列表与对应收件页,纯命令行抓取。
|
||||
71
运营中枢/参考资料/sora2_api_使用说明.md
Normal file
71
运营中枢/参考资料/sora2_api_使用说明.md
Normal file
@@ -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/秒(按分辨率)。
|
||||
@@ -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 个 |
|
||||
|
||||
@@ -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) |
|
||||
|
||||
Reference in New Issue
Block a user