diff --git a/02_卡人(水)/_团队成员/水桥/智能纪要/SKILL.md b/02_卡人(水)/_团队成员/水桥/智能纪要/SKILL.md index 4e6e5d30..aca9c2f6 100755 --- a/02_卡人(水)/_团队成员/水桥/智能纪要/SKILL.md +++ b/02_卡人(水)/_团队成员/水桥/智能纪要/SKILL.md @@ -172,6 +172,35 @@ python3 scripts/fetch_feishu_minutes.py --file "导出.txt" --title "产研团 - **Soul派对聊天记录** - **其他会议文字记录** +### 批量下载多场妙记 TXT(如「派对」「受」100 场) + +飞书没有「妙记列表」API,需先拿到**妙记链接列表**,再批量拉取 TXT。 + +**步骤 1:得到 URL 列表文件 `urls.txt`** + +- **方式 A(推荐)**:在飞书客户端或网页打开 **视频会议 → 妙记**,在列表里用搜索框输入「派对」或「受」(或「soul 派对」),得到筛选结果后,逐条点开每条记录,复制浏览器地址栏链接(形如 `https://cunkebao.feishu.cn/minutes/xxxxx`),每行一个粘贴到 `urls.txt`。 +- **方式 B**:若列表页支持「复制链接」或导出,可一次性整理成每行一个 URL 的文本。 + +**步骤 2:批量下载 TXT** + +```bash +cd /Users/karuo/Documents/个人/卡若AI/02_卡人(水)/_团队成员/水桥/智能纪要/scripts + +# 从 urls.txt 批量下载,TXT 保存到默认 output 目录 +python3 batch_download_minutes_txt.py --list urls.txt + +# 指定输出目录(如 soul 派对 100 场) +python3 batch_download_minutes_txt.py --list urls.txt --output ./soul_party_100_txt + +# 已下载过的跳过,避免重复 +python3 batch_download_minutes_txt.py --list urls.txt --output ./soul_party_100_txt --skip-existing + +# 先试跑前 3 条 +python3 batch_download_minutes_txt.py --list urls.txt --limit 3 +``` + +**说明**:脚本内部调用飞书妙记 API 拉取文字记录;若某条无「妙记文字记录」权限,该条会保存为仅含标题+时长的占位 TXT,可后续在妙记页手动「导出文字记录」后替换。 + --- ## 📤 飞书集成配置 diff --git a/02_卡人(水)/_团队成员/水桥/智能纪要/references/飞书妙记批量下载TXT说明.md b/02_卡人(水)/_团队成员/水桥/智能纪要/references/飞书妙记批量下载TXT说明.md new file mode 100644 index 00000000..eee7ce8d --- /dev/null +++ b/02_卡人(水)/_团队成员/水桥/智能纪要/references/飞书妙记批量下载TXT说明.md @@ -0,0 +1,57 @@ +# 飞书妙记批量下载 TXT 说明 + +> 场景:飞书妙记里已有多场带「派对」「受」等关键字的视频会议(如 soul 派对 100 场),需要把这批会议的文字记录**全部下载为 TXT**。 + +## 一、为什么不能「一键筛 100 场再导出」 + +- 飞书开放平台**没有**「妙记列表」或「按标题筛选妙记」的 API,只能通过**已知的妙记链接(minute_token)**逐条拉取。 +- 因此流程是:**先拿到这批妙记的 URL 列表 → 再用脚本批量拉取 TXT**。 + +## 二、如何拿到 URL 列表(urls.txt) + +### 方法 1:在飞书妙记列表里手动收集(推荐) + +1. 打开飞书(客户端或网页)→ **视频会议** → **妙记**。 +2. 在列表页的**搜索框**输入关键字,例如:`派对`、`受`、`soul 派对`,得到筛选后的列表。 +3. 逐条点开每条记录,在浏览器地址栏复制链接(形如 `https://xxx.feishu.cn/minutes/xxxxxxxxxx`),粘贴到文本文件,**每行一个链接**,保存为 `urls.txt`。 +4. 若使用飞书客户端,可尝试「在浏览器中打开」当前妙记,再复制地址栏。 + +### 方法 2:只写 minute_token(也可以) + +若链接形式是 `https://cunkebao.feishu.cn/minutes/obcnjnsx2mz7vj5q843172p8`,脚本也支持在 `urls.txt` 里只写后半段 token,例如一行写 `obcnjnsx2mz7vj5q843172p8` 即可。 + +## 三、批量下载命令 + +在智能纪要脚本目录下执行: + +```bash +cd /Users/karuo/Documents/个人/卡若AI/02_卡人(水)/_团队成员/水桥/智能纪要/scripts + +# 批量下载,输出到默认 output +python3 batch_download_minutes_txt.py --list urls.txt + +# 输出到指定目录(如 soul 派对 100 场) +python3 batch_download_minutes_txt.py --list urls.txt --output ./soul_party_100_txt + +# 已存在同名 TXT 则跳过(断点续跑) +python3 batch_download_minutes_txt.py --list urls.txt --output ./soul_party_100_txt --skip-existing + +# 先试跑 3 条 +python3 batch_download_minutes_txt.py --list urls.txt --limit 3 +``` + +## 四、输出说明 + +- 每条妙记会生成一个 TXT,文件名格式:`标题_日期.txt`(非法字符会替换为下划线)。 +- 若应用没有「妙记文字记录」权限,该条会保存为仅含标题+时长的占位内容;可在飞书妙记页对该条手动「…」→「导出文字记录」后,用导出的文件覆盖或合并。 + +## 五、相关脚本 + +| 脚本 | 作用 | +|:---|:---| +| `batch_download_minutes_txt.py` | 从 urls.txt 批量拉取 TXT | +| `fetch_feishu_minutes.py` | 单条妙记链接 → 拉取并保存 TXT(被批量脚本复用) | + +--- + +**版本**:2026-02-16 | 归属:水桥 · 智能纪要 diff --git a/02_卡人(水)/_团队成员/水桥/智能纪要/scripts/batch_download_minutes_txt.py b/02_卡人(水)/_团队成员/水桥/智能纪要/scripts/batch_download_minutes_txt.py new file mode 100644 index 00000000..ce604184 --- /dev/null +++ b/02_卡人(水)/_团队成员/水桥/智能纪要/scripts/batch_download_minutes_txt.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +""" +飞书妙记批量下载 TXT 文字记录 + +从「URL 列表文件」批量拉取飞书妙记,将每场的文字记录保存为 TXT。 +适用于:派对、受 等关键字筛选后的 100 场妙记一次性下载。 + +用法: + # 从 urls.txt 批量下载(每行一个妙记链接) + python3 batch_download_minutes_txt.py --list urls.txt + + # 指定输出目录 + python3 batch_download_minutes_txt.py --list urls.txt --output ./soul_party_txt + + # 跳过已存在的 TXT(按标题+日期判断) + python3 batch_download_minutes_txt.py --list urls.txt --skip-existing + + # 仅试跑前 3 条 + python3 batch_download_minutes_txt.py --list urls.txt --limit 3 + +如何得到 urls.txt: + 1) 在飞书妙记列表页搜索「派对」「受」或「soul 派对」,逐个打开每条记录,复制地址栏链接到文本,每行一个。 + 2) 或用浏览器自动化在列表页抓取所有卡片的链接(需在已登录飞书的前提下)。 +""" + +import argparse +import re +import sys +from datetime import datetime +from pathlib import Path + +SCRIPT_DIR = Path(__file__).parent +ROOT = SCRIPT_DIR.parent +OUTPUT_DIR = ROOT / "output" + +# 导入单条拉取逻辑 +sys.path.insert(0, str(SCRIPT_DIR)) +from fetch_feishu_minutes import ( + extract_minute_token, + fetch_and_save, + get_tenant_access_token, + get_minutes_info, + get_minutes_transcript, + get_minutes_speakers, + transcripts_to_text, + save_transcript, + format_timestamp, + OUTPUT_DIR as DEFAULT_OUTPUT_DIR, +) + + +def load_url_list(path: Path) -> list[str]: + """从文件读取 URL 列表,每行一个,去掉空行和重复""" + if not path.exists(): + return [] + lines = path.read_text(encoding="utf-8", errors="ignore").strip().splitlines() + urls = [] + seen = set() + for line in lines: + line = line.strip() + if not line or line.startswith("#"): + continue + # 兼容只写 minute_token 的情况 + if line not in seen: + seen.add(line) + urls.append(line) + return urls + + +def main(): + parser = argparse.ArgumentParser( + description="飞书妙记批量下载 TXT:从 URL 列表文件批量拉取文字记录" + ) + parser.add_argument( + "--list", "-l", + type=str, + required=True, + help="URL 列表文件路径,每行一个妙记链接或 minute_token", + ) + parser.add_argument( + "--output", "-o", + type=str, + default=None, + help=f"TXT 输出目录(默认: {OUTPUT_DIR})", + ) + parser.add_argument( + "--skip-existing", + action="store_true", + help="若输出目录已存在同名 TXT 则跳过该条", + ) + parser.add_argument( + "--limit", "-n", + type=int, + default=0, + help="仅处理前 N 条(0 表示全部)", + ) + args = parser.parse_args() + + list_path = Path(args.list) + if not list_path.is_absolute(): + list_path = (Path.cwd() / args.list).resolve() + if not list_path.exists(): + print(f"❌ 列表文件不存在: {list_path}") + sys.exit(1) + + output_dir = Path(args.output).resolve() if args.output else OUTPUT_DIR + output_dir.mkdir(parents=True, exist_ok=True) + print(f"📂 输出目录: {output_dir}") + + urls = load_url_list(list_path) + if not urls: + print("❌ 列表文件中没有有效 URL") + sys.exit(1) + + if args.limit: + urls = urls[: args.limit] + print(f"⚠️ 仅处理前 {len(urls)} 条(--limit {args.limit})") + print(f"📋 共 {len(urls)} 条妙记待下载\n") + + ok_count = 0 + skip_count = 0 + fail_count = 0 + token = get_tenant_access_token() + if not token: + print("❌ 无法获取飞书访问令牌") + sys.exit(1) + + for i, url_or_token in enumerate(urls, 1): + minute_token = extract_minute_token(url_or_token) + print(f"[{i}/{len(urls)}] 妙记 token: {minute_token[:20]}...") + + if args.skip_existing: + # 用 API 先取标题再判断文件是否已存在(避免重复请求) + info = get_minutes_info(token, minute_token) + if info: + title = info.get("title", "妙记") + safe_title = re.sub(r'[\\/*?:"<>|]', "_", title) + create_time = info.get("create_time", "") + if create_time: + try: + # 飞书 API 可能返回秒或毫秒时间戳,>1e10 视为毫秒 + ts = int(create_time) + if ts > 1e10: + ts = ts // 1000 + date_str = datetime.fromtimestamp(ts).strftime("%Y%m%d") + except Exception: + date_str = datetime.now().strftime("%Y%m%d") + else: + date_str = datetime.now().strftime("%Y%m%d") + existing = output_dir / f"{safe_title}_{date_str}.txt" + if existing.exists(): + print(f" ⏭️ 已存在,跳过: {existing.name}") + skip_count += 1 + continue + + out = fetch_and_save(url_or_token, output_dir) + if out and out.exists(): + ok_count += 1 + else: + fail_count += 1 + print("") + + print("=" * 50) + print(f"✅ 成功: {ok_count} | ⏭️ 跳过: {skip_count} | ❌ 失败: {fail_count}") + print(f"📂 所有 TXT 保存在: {output_dir}") + + +if __name__ == "__main__": + main() diff --git a/_共享模块/scripts/wiki_init_ssh.sh b/_共享模块/scripts/wiki_init_ssh.sh new file mode 100755 index 00000000..e960d087 --- /dev/null +++ b/_共享模块/scripts/wiki_init_ssh.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# 百科 SSH 初始化:在 NAS 上创建 karuo-ai.wiki.git 并推送 wiki_source 内容 +# 当 API/HTTPS 无法初始化时用此脚本。需能 SSH 到 Gitea 所在主机。 + +REPO_DIR="/Users/karuo/Documents/个人/卡若AI" +WIKI_SRC="$REPO_DIR/_共享模块/wiki_source" +SSH_HOST="open.quwanzhi.com" +SSH_PORT="22201" +SSH_USER="fnvtk" +WIKI_PATH="/volume1/git/github/fnvtk/karuo-ai.wiki.git" +SSH_REMOTE="ssh://${SSH_USER}@${SSH_HOST}:${SSH_PORT}/${WIKI_PATH}" + +set -e +cd "$REPO_DIR" + +# 1. SSH 创建 wiki bare 仓库(若不存在) +echo "在 NAS 上创建 wiki 仓库..." +ssh -o StrictHostKeyChecking=no -o ConnectTimeout=15 -p "$SSH_PORT" "${SSH_USER}@${SSH_HOST}" \ + "mkdir -p $WIKI_PATH && (test -f $WIKI_PATH/HEAD || (cd $WIKI_PATH && git init --bare))" + +# 2. 本地建临时仓库并推送 +TMP_WIKI=$(mktemp -d) +trap "rm -rf $TMP_WIKI" EXIT +cd "$TMP_WIKI" +git init -q +cp -f "$WIKI_SRC"/*.md . 2>/dev/null || true +git add -A +git commit -m "wiki init $(date '+%Y-%m-%d %H:%M')" --allow-empty -q +git remote add origin "$SSH_REMOTE" +git push -u origin master 2>/dev/null || { git branch -M main; git push -u origin main; } + +echo "百科已通过 SSH 初始化并推送完成。" +exit 0 diff --git a/_共享模块/工作台/gitea_push_log.md b/_共享模块/工作台/gitea_push_log.md index 3baf3754..3a97e1d2 100644 --- a/_共享模块/工作台/gitea_push_log.md +++ b/_共享模块/工作台/gitea_push_log.md @@ -10,3 +10,4 @@ | 2026-02-15 23:34:04 | 🔄 卡若AI 同步 2026-02-15 23:34 | 变更 7 个文件 | 排除 >20MB: 4 个 | | 2026-02-15 23:38:04 | 🔄 卡若AI 同步 2026-02-15 23:38 | 变更 7 个文件 | 排除 >20MB: 4 个 | | 2026-02-15 23:38:29 | 🔄 卡若AI 同步 2026-02-15 23:38 | 变更 4 个文件 | 排除 >20MB: 4 个 | +| 2026-02-15 23:43:11 | 🔄 卡若AI 同步 2026-02-15 23:43 | 变更 5 个文件 | 排除 >20MB: 4 个 | diff --git a/_共享模块/工作台/代码管理.md b/_共享模块/工作台/代码管理.md index e1f48fb6..5387445f 100644 --- a/_共享模块/工作台/代码管理.md +++ b/_共享模块/工作台/代码管理.md @@ -13,3 +13,4 @@ | 2026-02-15 23:34:04 | 成功 | 失败(百科未初始化或网络) | 🔄 卡若AI 同步 2026-02-15 23:34 | 变更 7 个文件 | 排除 >20MB: 4 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-15 23:38:04 | 成功 | 失败(百科未初始化或网络) | 🔄 卡若AI 同步 2026-02-15 23:38 | 变更 7 个文件 | 排除 >20MB: 4 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-15 23:38:29 | 成功 | 失败(百科未初始化或网络) | 🔄 卡若AI 同步 2026-02-15 23:38 | 变更 4 个文件 | 排除 >20MB: 4 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-02-15 23:43:11 | 成功 | 失败(百科未初始化或网络) | 🔄 卡若AI 同步 2026-02-15 23:43 | 变更 5 个文件 | 排除 >20MB: 4 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |