🔄 卡若AI 同步 2026-03-03 10:15 | 更新:水桥平台对接、卡木、火炬、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 14 个
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
- **脚本**:永平项目下 `scripts/send_chapter_poster_to_feishu.py`
|
||||
- **依赖**:`pip install requests Pillow`;飞书应用凭证写在 `scripts/.env.feishu`(FEISHU_APP_ID、FEISHU_APP_SECRET)。
|
||||
- **固定群 webhook**:脚本内置默认发到 **Soul 彩民团队** 飞书群,webhook 为 `https://open.feishu.cn/open-apis/bot/v2/hook/14a7e0d3-864d-4709-ad40-0def6edba566`。无需复制链接,直接运行命令即可。
|
||||
- **自定义 webhook(--webhook)**:用 `--webhook "https://..."` 可指定其他群。**推送前必须确认**:目标群名称含有「瘦」或「IP」,否则不要推送;脚本无法从 webhook 获知群名,需人工在飞书里核对。
|
||||
- **命令示例**(上传完成后执行):
|
||||
|
||||
```bash
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
| 项目 | 规范 |
|
||||
|:---|:---|
|
||||
| **「我」** | **整篇文章最多出现三次**。多用「这边」「直接」「就」「场上」等替代;成稿后全文搜索「我」,超过 3 处必须改写。 |
|
||||
| **「我」** | **整篇文章最多出现三次**(仅指叙述者视角;对话引用里的「我」不占名额)。成稿后全文搜索「我」,超过 3 处必须改写。 |
|
||||
| **「这边」「那边」** | **不要用**。改用灵活表述:房主、场上、就、手头、这种模式、知识星球/小程序 等指代或省略主语。 |
|
||||
| **「回答说」** | **不要用**。房主是强势角色,文章目的是让读者产生深度认同感;房主的话用**直接陈述**呈现,不写「回答说,……」,让语气与性格一致、话一出口就立住。 |
|
||||
| **「卡若」** | 每篇最多提一次;不需要时可完全不出现。 |
|
||||
|
||||
---
|
||||
@@ -47,7 +49,7 @@
|
||||
- **推进方式**:时间线或事件线,逻辑清晰(如「有人问 → 回答」「3 号问 → …」)
|
||||
- **分段**:每段一个主题,小主题隐于叙述中,不列段头小标题
|
||||
- **穿插**:细节、对话、观点分析
|
||||
- **多用对话**:增强真实感(「X 号问」「有人问」「直接回答」「这边说」等)
|
||||
- **多用对话**:增强真实感(「X 号问」「有人问」等);房主的话**直接接在问句后**,不写「回答说」,语气强势、可认同。
|
||||
- **分享句(两处,强制)**:约 20% 处一句、结尾一句,各不超过 50 字,围绕本节主题、紧扣内容,留余味或可执行。**不要出现「干货」二字**,不要用「干货:」或「**干货**:」等格式,直接写一句金句即可,可单独成段。
|
||||
|
||||
---
|
||||
|
||||
@@ -86,7 +86,7 @@ OUT_DIR = Path("/Users/karuo/Documents/聊天记录/soul")
|
||||
|
||||
|
||||
def run_playwright_page_export(from_num: int, to_num: int) -> int:
|
||||
"""在 Playwright 页面内直接请求导出,绕过 Cookie,保证成功。"""
|
||||
"""在 Playwright 用系统默认浏览器打开,页面内直接请求导出,复用已有登录/Cookie。"""
|
||||
import re
|
||||
items = [(n, t, tok, d) for n, t, tok, d in SOUL_ITEMS if from_num <= n <= to_num]
|
||||
if not items:
|
||||
@@ -96,12 +96,29 @@ def run_playwright_page_export(from_num: int, to_num: int) -> int:
|
||||
except ImportError:
|
||||
print("❌ 需安装 playwright: pip install playwright && playwright install chromium", file=sys.stderr)
|
||||
return 1
|
||||
import tempfile
|
||||
ud = tempfile.mkdtemp(prefix="feishu_export_")
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
try:
|
||||
from playwright_default_browser import launch_playwright_with_default_browser
|
||||
except ImportError:
|
||||
launch_playwright_with_default_browser = None
|
||||
try:
|
||||
with sync_playwright() as p:
|
||||
ctx = p.chromium.launch_persistent_context(ud, headless=False, timeout=15000)
|
||||
pg = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
if launch_playwright_with_default_browser:
|
||||
ctx, get_page, cleanup = launch_playwright_with_default_browser(p, headless=False, timeout=15000)
|
||||
pg = get_page()
|
||||
else:
|
||||
import tempfile
|
||||
ud = tempfile.mkdtemp(prefix="feishu_export_")
|
||||
ctx = p.chromium.launch_persistent_context(ud, headless=False, timeout=15000)
|
||||
pg = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
pass
|
||||
import shutil
|
||||
shutil.rmtree(ud, ignore_errors=True)
|
||||
pg.goto(MINUTES_URL, wait_until="domcontentloaded", timeout=25000)
|
||||
print(" ⚠️ 请在此窗口登录飞书妙记(看到列表即可),等待 90 秒…")
|
||||
time.sleep(90)
|
||||
@@ -128,47 +145,66 @@ def run_playwright_page_export(from_num: int, to_num: int) -> int:
|
||||
except Exception as e:
|
||||
print(f" ❌ {topic}: {e}")
|
||||
time.sleep(1)
|
||||
ctx.close()
|
||||
try:
|
||||
cleanup()
|
||||
except Exception:
|
||||
pass
|
||||
print(f"✅ 页面内导出完成 {saved}/{len(items)} 场,目录: {OUT_DIR}")
|
||||
return 0
|
||||
finally:
|
||||
import shutil
|
||||
shutil.rmtree(ud, ignore_errors=True)
|
||||
except Exception as e:
|
||||
print(f"❌ Playwright 导出异常: {e}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def collect_cookie_via_playwright_standalone() -> str:
|
||||
"""Playwright 启动独立 Chromium,用户登录后抓 Cookie。"""
|
||||
"""Playwright 用系统默认浏览器打开,用户登录后抓 Cookie,复用已有登录态。"""
|
||||
try:
|
||||
from playwright.sync_api import sync_playwright
|
||||
except ImportError:
|
||||
return ""
|
||||
import tempfile
|
||||
ud = tempfile.mkdtemp(prefix="feishu_cookie_")
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
try:
|
||||
from playwright_default_browser import launch_playwright_with_default_browser
|
||||
except ImportError:
|
||||
launch_playwright_with_default_browser = None
|
||||
cookie_str = ""
|
||||
try:
|
||||
with sync_playwright() as p:
|
||||
ctx = p.chromium.launch_persistent_context(
|
||||
user_data_dir=ud,
|
||||
headless=False,
|
||||
args=["--start-maximized"],
|
||||
viewport={"width": 1280, "height": 900},
|
||||
timeout=15000,
|
||||
)
|
||||
try:
|
||||
if launch_playwright_with_default_browser:
|
||||
ctx, get_page, cleanup = launch_playwright_with_default_browser(p, headless=False, timeout=15000)
|
||||
pg = get_page()
|
||||
else:
|
||||
import tempfile
|
||||
ud = tempfile.mkdtemp(prefix="feishu_cookie_")
|
||||
ctx = p.chromium.launch_persistent_context(
|
||||
user_data_dir=ud,
|
||||
headless=False,
|
||||
args=["--start-maximized"],
|
||||
viewport={"width": 1280, "height": 900},
|
||||
timeout=15000,
|
||||
)
|
||||
pg = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
pass
|
||||
import shutil
|
||||
shutil.rmtree(ud, ignore_errors=True)
|
||||
try:
|
||||
pg.goto(MINUTES_URL, wait_until="domcontentloaded", timeout=25000)
|
||||
except Exception:
|
||||
pg = ctx.new_page() if not ctx.pages else ctx.pages[0]
|
||||
try:
|
||||
pg.goto(MINUTES_URL, wait_until="domcontentloaded", timeout=25000)
|
||||
except Exception:
|
||||
pass
|
||||
pass
|
||||
print(" ⚠️ 请在此窗口完成飞书妙记登录(输入账号密码直到看到列表),等待 120 秒…")
|
||||
time.sleep(120)
|
||||
cookies = ctx.cookies("https://cunkebao.feishu.cn")
|
||||
if not cookies:
|
||||
cookies = ctx.cookies()
|
||||
ctx.close()
|
||||
try:
|
||||
cleanup()
|
||||
except Exception:
|
||||
pass
|
||||
seen = set()
|
||||
parts = []
|
||||
for c in cookies:
|
||||
@@ -176,12 +212,8 @@ def collect_cookie_via_playwright_standalone() -> str:
|
||||
seen.add(c["name"])
|
||||
parts.append(f"{c['name']}={c.get('value','')}")
|
||||
cookie_str = "; ".join(parts)
|
||||
finally:
|
||||
import shutil
|
||||
try:
|
||||
shutil.rmtree(ud, ignore_errors=True)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f" Playwright 取 Cookie 失败: {e}", file=sys.stderr)
|
||||
return cookie_str if len(cookie_str) > 200 else ""
|
||||
|
||||
|
||||
|
||||
@@ -71,18 +71,37 @@ def export_via_cookie(object_token: str) -> str | None:
|
||||
|
||||
|
||||
def export_via_playwright_page(object_token: str, title: str = "", wait_sec: int = 30) -> str | None:
|
||||
"""Playwright 打开妙记页,在页面内 fetch 导出接口(带 credentials),无感拿正文。"""
|
||||
"""Playwright 用系统默认浏览器打开妙记页,在页面内 fetch 导出接口(带 credentials),复用已有登录/Cookie。"""
|
||||
try:
|
||||
from playwright.sync_api import sync_playwright
|
||||
except ImportError:
|
||||
return None
|
||||
import tempfile
|
||||
ud = tempfile.mkdtemp(prefix="feishu_one_")
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
try:
|
||||
from playwright_default_browser import launch_playwright_with_default_browser
|
||||
except ImportError:
|
||||
launch_playwright_with_default_browser = None
|
||||
result = [None]
|
||||
cleanup = lambda: None
|
||||
|
||||
try:
|
||||
with sync_playwright() as p:
|
||||
ctx = p.chromium.launch_persistent_context(ud, headless=False, timeout=15000)
|
||||
pg = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
if launch_playwright_with_default_browser:
|
||||
ctx, get_page, cleanup = launch_playwright_with_default_browser(p, headless=False, timeout=15000)
|
||||
pg = get_page()
|
||||
else:
|
||||
import tempfile
|
||||
ud = tempfile.mkdtemp(prefix="feishu_one_")
|
||||
ctx = p.chromium.launch_persistent_context(ud, headless=False, timeout=15000)
|
||||
pg = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
pass
|
||||
import shutil
|
||||
shutil.rmtree(ud, ignore_errors=True)
|
||||
pg.goto(f"https://cunkebao.feishu.cn/minutes/{object_token}", wait_until="domcontentloaded", timeout=25000)
|
||||
print(f" 页面已打开,等待 {wait_sec} 秒(若未登录请先登录)…")
|
||||
time.sleep(wait_sec)
|
||||
@@ -115,11 +134,38 @@ def export_via_playwright_page(object_token: str, title: str = "", wait_sec: int
|
||||
result[0] = text
|
||||
elif len(text) > 400:
|
||||
result[0] = text
|
||||
# 兜底:从页面 DOM 抓取正文(先点「文字记录」再取整页或大块文本)
|
||||
if not result[0]:
|
||||
try:
|
||||
# 尝试点击「文字记录」tab 再取内容
|
||||
for label in ["文字记录", "文字"]:
|
||||
try:
|
||||
pg.get_by_text(label, exact=False).first.click(timeout=3000)
|
||||
time.sleep(1.5)
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
dom_text = pg.evaluate("""() => {
|
||||
let out = '';
|
||||
const candidates = document.querySelectorAll('[class*="content"], [class*="paragraph"], [class*="segment"], [class*="transcript"], [class*="record"], .ne-doc-body, [role="main"]');
|
||||
for (const el of candidates) {
|
||||
const t = (el.innerText || el.textContent || '').trim();
|
||||
if (t.length > 800 && (t.includes(':') || t.includes(':') || /\\d{1,2}:\\d{2}:\\d{2}/.test(t))) { out = t; break; }
|
||||
}
|
||||
if (!out) out = document.body.innerText || document.body.textContent || '';
|
||||
return out;
|
||||
}""")
|
||||
if dom_text and len(dom_text.strip()) > 300:
|
||||
result[0] = dom_text.strip()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f" Playwright 失败: {e}", file=sys.stderr)
|
||||
finally:
|
||||
import shutil
|
||||
shutil.rmtree(ud, ignore_errors=True)
|
||||
try:
|
||||
cleanup()
|
||||
except Exception:
|
||||
pass
|
||||
return result[0]
|
||||
|
||||
|
||||
|
||||
192
02_卡人(水)/水桥_平台对接/智能纪要/脚本/playwright_default_browser.py
Normal file
192
02_卡人(水)/水桥_平台对接/智能纪要/脚本/playwright_default_browser.py
Normal file
@@ -0,0 +1,192 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
检测系统默认浏览器,并用该浏览器启动 Playwright,优先使用其用户数据目录以保留登录/Cookie。
|
||||
供 feishu_minutes_one_url、auto_cookie_and_export、逆向获取Cookie并下载单条 等脚本使用。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_system_default_browser():
|
||||
"""
|
||||
检测系统当前使用的默认浏览器。
|
||||
返回 (engine, channel, profile_path):
|
||||
- engine: "chromium" | "webkit" | "firefox"
|
||||
- channel: 仅 chromium 时有效,"chrome" | "msedge" | "chromium" | None
|
||||
- profile_path: 用户数据目录,用于复用登录态;None 表示不使用系统 profile
|
||||
"""
|
||||
system = platform.system()
|
||||
home = Path.home()
|
||||
|
||||
if system == "Darwin": # macOS
|
||||
# 优先读取系统默认 HTTP/HTTPS 处理程序
|
||||
try:
|
||||
out = subprocess.run(
|
||||
["defaults", "read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
)
|
||||
if out.returncode == 0 and out.stdout:
|
||||
for line in out.stdout.splitlines():
|
||||
if "https" in line.lower() or "http" in line.lower():
|
||||
continue
|
||||
# 解析 LSHandlers 较复杂,改为按应用存在性检测
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 按优先级检测已安装的浏览器(与多数用户习惯一致:Chrome/Edge 常用)
|
||||
apps = [
|
||||
("Google Chrome", "chromium", "chrome", home / "Library/Application Support/Google/Chrome"),
|
||||
("Microsoft Edge", "chromium", "msedge", home / "Library/Application Support/Microsoft Edge"),
|
||||
("Chromium", "chromium", "chromium", home / "Library/Application Support/Chromium"),
|
||||
("Safari", "webkit", None, None),
|
||||
("Firefox", "firefox", None, home / "Library/Application Support/Firefox"),
|
||||
]
|
||||
for app_name, engine, channel, profile in apps:
|
||||
app_path = Path(f"/Applications/{app_name}.app")
|
||||
if app_path.exists():
|
||||
if engine == "firefox" and profile:
|
||||
# Firefox 使用 Profiles/xxx.default 子目录
|
||||
profiles = profile / "Profiles"
|
||||
if profiles.exists():
|
||||
for d in profiles.iterdir():
|
||||
if d.is_dir() and (d / "prefs.js").exists():
|
||||
profile = d
|
||||
break
|
||||
else:
|
||||
profile = None
|
||||
out_profile = str(profile) if profile and profile.exists() else None
|
||||
return (engine, channel, out_profile)
|
||||
# 若都未找到,退回 Chromium(Playwright 自带)
|
||||
return ("chromium", None, None)
|
||||
|
||||
if system == "Windows":
|
||||
try:
|
||||
out = subprocess.run(
|
||||
["reg", "query", "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\https\\UserChoice", "/v", "ProgId"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
)
|
||||
if out.returncode == 0 and "chrome" in out.stdout.lower():
|
||||
return ("chromium", "chrome", None)
|
||||
if out.returncode == 0 and "edge" in out.stdout.lower():
|
||||
return ("chromium", "msedge", None)
|
||||
except Exception:
|
||||
pass
|
||||
return ("chromium", None, None)
|
||||
|
||||
return ("chromium", None, None)
|
||||
|
||||
|
||||
def launch_playwright_with_default_browser(sync_playwright, headless: bool = False, timeout: int = 15000):
|
||||
"""
|
||||
使用系统默认浏览器启动 Playwright,返回 (context_or_browser, page_getter, cleanup)。
|
||||
page_getter 用于获取当前 page(可能是 context.pages[0] 或 new_page)。
|
||||
cleanup 用于关闭 context/browser。
|
||||
若使用持久 context,则尽量复用系统 profile,避免 Cookie 无法登录。
|
||||
"""
|
||||
import tempfile
|
||||
p = sync_playwright
|
||||
engine, channel, profile_path = get_system_default_browser()
|
||||
|
||||
if engine == "chromium":
|
||||
channel_info = f" (channel={channel})" if channel else ""
|
||||
print(f" 使用系统浏览器: Chromium 系{channel_info},profile={profile_path or '临时目录'}")
|
||||
|
||||
# 优先尝试用系统 profile 启动(已登录则直接用)
|
||||
if profile_path and channel:
|
||||
try:
|
||||
ctx = p.chromium.launch_persistent_context(
|
||||
profile_path,
|
||||
channel=channel,
|
||||
headless=headless,
|
||||
timeout=timeout,
|
||||
args=["--no-first-run"],
|
||||
)
|
||||
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return ctx, lambda: page, cleanup
|
||||
except Exception as e:
|
||||
if "already in use" in str(e).lower() or "User data directory" in str(e):
|
||||
print(f" 系统浏览器正在使用中,改用临时目录(请在新窗口内登录一次): {e}")
|
||||
else:
|
||||
print(f" 使用系统 profile 失败,改用临时目录: {e}")
|
||||
|
||||
# 使用临时目录 + 指定 channel(仍为系统安装的 Chrome/Edge)
|
||||
user_data = tempfile.mkdtemp(prefix="feishu_playwright_")
|
||||
try:
|
||||
kwargs = {"headless": headless, "timeout": timeout}
|
||||
if channel:
|
||||
kwargs["channel"] = channel
|
||||
ctx = p.chromium.launch_persistent_context(user_data, **kwargs)
|
||||
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
pass
|
||||
import shutil
|
||||
shutil.rmtree(user_data, ignore_errors=True)
|
||||
|
||||
return ctx, lambda: page, cleanup
|
||||
except Exception:
|
||||
import shutil
|
||||
shutil.rmtree(user_data, ignore_errors=True)
|
||||
raise
|
||||
|
||||
if engine == "webkit":
|
||||
print(" 使用系统浏览器: Safari (WebKit)")
|
||||
browser = p.webkit.launch(headless=headless)
|
||||
ctx = browser
|
||||
page = browser.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
browser.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return ctx, lambda: page, cleanup
|
||||
|
||||
if engine == "firefox":
|
||||
print(" 使用系统浏览器: Firefox")
|
||||
browser = p.firefox.launch(headless=headless)
|
||||
ctx = browser
|
||||
page = browser.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
browser.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return ctx, lambda: page, cleanup
|
||||
|
||||
# 默认
|
||||
print(" 使用 Playwright 自带 Chromium")
|
||||
user_data = tempfile.mkdtemp(prefix="feishu_playwright_")
|
||||
ctx = p.chromium.launch_persistent_context(user_data, headless=headless, timeout=timeout)
|
||||
page = ctx.pages[0] if ctx.pages else ctx.new_page()
|
||||
|
||||
def cleanup():
|
||||
try:
|
||||
ctx.close()
|
||||
except Exception:
|
||||
pass
|
||||
import shutil
|
||||
shutil.rmtree(user_data, ignore_errors=True)
|
||||
|
||||
return ctx, lambda: page, cleanup
|
||||
35
02_卡人(水)/水桥_平台对接/智能纪要/脚本/浏览器控制台_复制妙记文字.js
Normal file
35
02_卡人(水)/水桥_平台对接/智能纪要/脚本/浏览器控制台_复制妙记文字.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 在已打开的飞书妙记页面(cunkebao.feishu.cn/minutes/xxx)按 F12 → Console 粘贴整段运行,
|
||||
* 会提取当前页「文字记录」区域文字并复制到剪贴板,同时打印到控制台。
|
||||
*/
|
||||
(function () {
|
||||
function getText() {
|
||||
const candidates = document.querySelectorAll(
|
||||
'[class*="content"], [class*="paragraph"], [class*="segment"], [class*="transcript"], [class*="record"], .ne-doc-body, [role="main"]'
|
||||
);
|
||||
for (const el of candidates) {
|
||||
const t = (el.innerText || el.textContent || "").trim();
|
||||
if (t.length > 800 && (t.includes(":") || t.includes(":") || /\d{1,2}:\d{2}:\d{2}/.test(t))) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return (document.body && (document.body.innerText || document.body.textContent)) || "";
|
||||
}
|
||||
const text = getText().trim();
|
||||
if (!text) {
|
||||
console.warn("未找到长文本,请先点击「文字记录」选项卡再运行本脚本。");
|
||||
return;
|
||||
}
|
||||
const title = (document.title || "妙记").replace(/\s*\|\s*.*$/, "");
|
||||
const out = "标题: " + title + "\n\n文字记录:\n\n" + text;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(out).then(function () {
|
||||
console.log("已复制到剪贴板,共 " + out.length + " 字。可粘贴到记事本保存。");
|
||||
}).catch(function () {
|
||||
console.log(out.slice(0, 500) + "\n...(共 " + out.length + " 字,请手动选择上方输出复制)");
|
||||
});
|
||||
} else {
|
||||
console.log(out.slice(0, 2000) + "\n...(共 " + out.length + " 字)");
|
||||
}
|
||||
console.log("前 500 字预览:", out.slice(0, 500));
|
||||
})();
|
||||
@@ -122,7 +122,7 @@ def main() -> int:
|
||||
try:
|
||||
from feishu_minutes_one_url import export_via_playwright_page
|
||||
print(" Cookie 导出 401,改用 Playwright 页面内获取…")
|
||||
text = export_via_playwright_page(token, title=title, wait_sec=45)
|
||||
text = export_via_playwright_page(token, title=title, wait_sec=60)
|
||||
except Exception as e:
|
||||
print(" Playwright 兜底失败:", e, file=sys.stderr)
|
||||
if text:
|
||||
|
||||
@@ -347,6 +347,14 @@ python3 scripts/feishu_wiki_create_doc.py --parent KNf7wA8Rki1NSdkkSIqcdFtTnWb -
|
||||
|
||||
JSON 格式:与 `团队入职流程与新人登记表_feishu_blocks.json` 相同,含 `children` 数组(飞书 docx blocks)。
|
||||
|
||||
### 读书笔记发飞书(链接与子目录)
|
||||
|
||||
- **原则**:读书笔记写完后同步发到飞书知识库,发到**指定链接(父节点)下的对应子目录**。
|
||||
- **链接分析**:飞书知识库链接格式为 `https://cunkebao.feishu.cn/wiki/<node_token>`,其中 `wiki/` 后的 `node_token` 即为父节点;若读书笔记放在「读书笔记」节点下,则用该节点 token 为 parent;若其下还有分类子目录(个人提升/创业/商业思维/投资/人际关系),则先在对应子节点下再建文档。
|
||||
- **目录结构建议**:父节点 = 读书笔记根(如「2、我写的日记」或「读书笔记」)→ 其下可建子目录「读书笔记」或按分类建「个人提升」「创业」等 → 单篇笔记为该目录下的一篇文档。
|
||||
- **执行**:用统一文章上传脚本,`--parent` 取目标父节点 token(即链接中的 node_token),`--title` 与本地笔记标题一致,`--md` 指向 `个人/2、我写的日记/读书笔记/xxx.md`。若需按分类落子目录,需先有该分类子节点 token,再以该 token 为 parent 创建文档。
|
||||
- **配置**:读书笔记默认本地路径为 `个人/2、我写的日记/读书笔记/`;飞书父节点 token 可配置为环境变量 `FEISHU_READING_WIKI_PARENT` 或写在读书笔记 Skill 的「飞书读书笔记配置」中;用户提供飞书「读书笔记」节点链接后,从链接提取 token 填入即可。
|
||||
|
||||
---
|
||||
|
||||
## 飞书导出 JSON 按原格式上传
|
||||
|
||||
77
02_卡人(水)/水桥_平台对接/飞书管理/脚本/write_0303_feishu_log.py
Normal file
77
02_卡人(水)/水桥_平台对接/飞书管理/脚本/write_0303_feishu_log.py
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
写入 3月3日 飞书日志到 3 月文档。昨日目标与今年总目标一致,百分比按总目标;今日 20 条视频 + 1 朋友圈,视频 Skill 四屏切片完成 20 个视频。
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from auto_log import get_token_silent, write_log, open_result, CONFIG
|
||||
|
||||
|
||||
def _get_march_wiki_token():
|
||||
raw = (CONFIG.get("MONTH_WIKI_TOKENS") or {}).get(3) or os.environ.get("FEISHU_MARCH_WIKI_TOKEN") or ""
|
||||
return (raw or "").strip() or None
|
||||
|
||||
|
||||
def build_tasks_0303():
|
||||
"""3月3日:昨日目标一致、总目标一致、百分比按此;今日 20 条视频 + 1 朋友圈;视频 Skill 四屏切片 20 个;百分比。"""
|
||||
return [
|
||||
{
|
||||
"person": "卡若",
|
||||
"events": ["今日复盘", "本月与最终目标", "今日核心", "视频Skill四屏切片"],
|
||||
"quadrant": "重要紧急",
|
||||
"t_targets": [
|
||||
"昨日目标与今年总目标一致,百分比按总目标执行",
|
||||
"本月目标约 12%,距最终目标差 88%",
|
||||
"今日核心:每天 20 条 Soul 视频 + 20:00 发 1 条朋友圈",
|
||||
"视频 Skill 四屏切片:完成 20 个视频(当日完成度见反馈)",
|
||||
],
|
||||
"n_process": [
|
||||
"【复盘】昨日目标一致、今年总目标一致,百分比按 2026年整体目标 对齐",
|
||||
"【2月突破执行】延续 3 月,本月/最终目标百分比已按进度写入",
|
||||
"【今日】20 条视频(四屏切片)+ 1 条朋友圈;视频切片 Skill 操作执行",
|
||||
],
|
||||
"t_thoughts": [
|
||||
"今日一条核心:20 条 Soul 视频 + 8 点 1 条朋友圈,持续拉齐与最终目标",
|
||||
"四屏切片完成 20 个视频,按当日完成数看百分比",
|
||||
],
|
||||
"w_work": [
|
||||
"20 条 Soul 视频(四屏切片)",
|
||||
"20:00 发 1 条朋友圈",
|
||||
"视频 Skill 操作",
|
||||
"飞书日志",
|
||||
],
|
||||
"f_feedback": [
|
||||
"本月/最终目标 12% / 100%,差 88%",
|
||||
"今日核心→20 条 Soul + 8 点朋友圈 🔄",
|
||||
"四屏切片 20 条→当日完成度待填 % 🔄",
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
token = get_token_silent()
|
||||
if not token:
|
||||
print("❌ 无法获取飞书 Token")
|
||||
sys.exit(1)
|
||||
march_token = _get_march_wiki_token()
|
||||
if not march_token:
|
||||
print("❌ 未配置 3 月文档,请设置 FEISHU_MARCH_WIKI_TOKEN")
|
||||
sys.exit(1)
|
||||
tasks = build_tasks_0303()
|
||||
ok = write_log(token, "3月3日", tasks, march_token, overwrite=False)
|
||||
if ok:
|
||||
open_result(march_token)
|
||||
print("✅ 3月3日 飞书日志已写入")
|
||||
else:
|
||||
print("❌ 写入失败")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
## 二、视频结构:提问→回答 + 前3秒高光 + 去语助词
|
||||
|
||||
- **前3秒**:先看片段有没有人提问;**有提问**则把**提问的问题**放到前3秒(封面/前贴),先展示问题再播回答;无提问则用金句/悬念作 hook。
|
||||
- **每个话题前均优先提问→回答**:先看片段有没有人提问;**有提问**则把**提问的问题**放到前3秒(封面/前贴),先展示问题再播回答;无提问则用金句/悬念作 hook。
|
||||
- **成片链路**:前3秒展示问题(或金句)→ 正片回答 → **整片去除语助词**(提问与回答部分均由 soul_enhance 清理)。
|
||||
- **高光**:按「3秒高光亮点」剪,每段 30~300 秒完整语义单元;高光识别若有提问须填 `question`,且 `hook_3sec` 与之一致。
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
|
||||
## 五、成片:封面 + 字幕 + 竖屏
|
||||
|
||||
- **封面**:竖屏 498×1080 内**不超出界面**;深色渐变背景(墨绿→绿)、左上角 Soul logo、标题文字**严格居中**且左右留白 44px,多行自动换行不裁切。
|
||||
- **字幕**:封面结束后才显示,**居中**在竖屏内;语助词由 soul_enhance 统一清理。重新加字幕时加 `--force-burn-subs`。
|
||||
- **封面**:竖屏 498×1080 内**不超出界面**;**半透明质感**(背景 alpha=165,透出底层画面);深色渐变(墨绿→绿)、左上角 Soul logo、标题文字**严格居中**且左右留白 44px,多行自动换行不裁切。透明度在 `soul_enhance.py` 中由 `VERTICAL_COVER_ALPHA` 调节(0~255)。
|
||||
- **字幕**:封面结束后才显示,**居中**在竖屏内;烧录用**图像 overlay**(每张字幕图 `-loop 1` + `enable=between(t,a,b)`),若系统 FFmpeg 带 libass 可改用 SRT+subtitles 滤镜;语助词由 soul_enhance 统一清理。重新加字幕时加 `--force-burn-subs`。
|
||||
- **竖屏**:498×1080,crop 参数与 `参考资料/竖屏中段裁剪参数说明.md` 一致
|
||||
|
||||
---
|
||||
|
||||
13
03_卡木(木)/木叶_视频内容/视频切片/参考资料/README.md
Normal file
13
03_卡木(木)/木叶_视频内容/视频切片/参考资料/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 视频切片 · 参考资料
|
||||
|
||||
本目录为视频切片 Skill 的参考资料,若出现「Unable to open 高光识别提示词.md」类错误,请用下面方式之一打开:
|
||||
|
||||
- **本目录下的文件**:[高光识别提示词.md](./高光识别提示词.md)
|
||||
- **快速打开**:在 Cursor 中 `Cmd+P`(或 Ctrl+P),输入 `高光识别提示词`,选择 `参考资料/高光识别提示词.md`
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| 高光识别提示词.md | AI 高光识别用提示词(Hook/CTA/提问→回答) |
|
||||
| 视频结构_提问回答与高光.md | 提问→回答结构、前3秒、去语助词 |
|
||||
| 竖屏中段裁剪参数说明.md | Soul 竖屏 498×1080 crop 参数 |
|
||||
| 主题片段提取规则.md | 按章节主题提取片段规则 |
|
||||
@@ -1,6 +1,7 @@
|
||||
# 高光识别提示词
|
||||
|
||||
> 所有输出(Hook、CTA、标题)统一**简体中文**。
|
||||
> 所有输出(Hook、CTA、标题)统一**简体中文**。
|
||||
> 本文件路径:`视频切片/参考资料/高光识别提示词.md`(卡若AI 项目内)
|
||||
|
||||
用于分析视频文字稿,识别最有传播力的片段,并生成前3秒Hook和结尾CTA。
|
||||
|
||||
@@ -48,9 +49,9 @@
|
||||
- 吊人胃口的开场
|
||||
- "接下来这段更精彩"
|
||||
|
||||
# 提问→回答 结构(前3秒优先用提问)
|
||||
# 提问→回答 结构(每个话题前均优先)
|
||||
|
||||
**成片结构**:先展示**提问**(前3秒),再进入**回答**(正片)。若片段里有人提问,必须把**提问的问题**放到前3秒。
|
||||
**每个话题/高光片段均优先采用提问→回答形式**:先展示**提问**(前3秒),再进入**回答**(正片)。若片段里有人提问,必须把**提问的问题**放到前3秒。
|
||||
|
||||
1. **判断**:看该高光片段文字稿中是否有人提问(观众/连麦者问的问题)。
|
||||
2. **若有提问**:
|
||||
|
||||
@@ -159,7 +159,7 @@ def _build_prompt(transcript: str, clip_count: int) -> str:
|
||||
txt = transcript[:5000] if len(transcript) > 5000 else transcript
|
||||
return f"""识别视频文字稿中的 {clip_count} 个高光片段,直接输出 JSON 数组,第一个字符必须是 [。
|
||||
|
||||
重要:若某片段里有人提问(观众/连麦者问的问题),必须提取提问内容填 question,且 hook_3sec 用该提问。成片前3秒先展示提问,再播回答。
|
||||
重要:每个话题均优先提问→回答。若某片段里有人提问(观众/连麦者问的问题),必须提取提问内容填 question,且 hook_3sec 用该提问;成片前3秒先展示提问,再播回答。
|
||||
|
||||
示例(有提问):
|
||||
[{{"title":"普通人怎么敢跟ZF搞","start_time":"01:12:30","end_time":"01:15:30","question":"普通人怎么敢跟ZF搞?","hook_3sec":"普通人怎么敢跟ZF搞?","cta_ending":"{DEFAULT_CTA}","transcript_excerpt":"维权起头跑通就成生意","reason":"提问+回答完整"}}]
|
||||
|
||||
@@ -89,6 +89,13 @@ FILLER_WORDS = [
|
||||
'怎么说呢', '你知道吗', '我跟你说', '好', '对', 'OK', 'ok',
|
||||
]
|
||||
|
||||
# 不烧录的无关/模板句(仅整句完全匹配或极短规则说明,避免误杀正片对白)
|
||||
SKIP_SUBTITLE_PHRASES = (
|
||||
"回复1", "回复 1", "排序分享", "上麦后按格式介绍自己", "进资源泡泡群", "做矩阵切片",
|
||||
"合作私聊", "群主必", "时间5~10分钟", "我能帮到大家什么", "我需要什么帮助",
|
||||
"廿四先生", "进来陪你聊天", "建房领开工红包",
|
||||
)
|
||||
|
||||
# 关键词高亮(重点突出,按长度排序避免短词覆盖长词)
|
||||
KEYWORDS = [
|
||||
'100万', '50万', '30万', '10万', '5万', '1万',
|
||||
@@ -118,6 +125,8 @@ SOUL_GREEN_DARK = (0, 160, 80)
|
||||
VERTICAL_COVER_TOP = (12, 32, 24) # 深墨绿
|
||||
VERTICAL_COVER_BOTTOM = (8, 48, 36) # 略亮绿
|
||||
VERTICAL_COVER_PADDING = 44 # 左右留白,保证文字不贴边、不超出
|
||||
# 成片封面半透明质感:背景层 alpha,便于透出底层画面
|
||||
VERTICAL_COVER_ALPHA = 165 # 0~255,越大越不透明
|
||||
|
||||
# 样式配置
|
||||
STYLE = {
|
||||
@@ -243,6 +252,53 @@ def parse_srt_for_clip(srt_path, start_sec, end_sec):
|
||||
|
||||
return subtitles
|
||||
|
||||
|
||||
def _filter_relevant_subtitles(subtitles):
|
||||
"""只过滤整句为规则/模板的条目,保留所有对白与重复句以便字幕连续"""
|
||||
out = []
|
||||
for sub in subtitles:
|
||||
text = (sub.get("text") or "").strip()
|
||||
if len(text) < 2:
|
||||
continue
|
||||
if text in SKIP_SUBTITLE_PHRASES:
|
||||
continue
|
||||
out.append(sub)
|
||||
return out
|
||||
|
||||
|
||||
def _sec_to_srt_time(sec):
|
||||
"""秒数转为 SRT 时间格式 HH:MM:SS,mmm"""
|
||||
h = int(sec) // 3600
|
||||
m = (int(sec) % 3600) // 60
|
||||
s = int(sec) % 60
|
||||
ms = int((sec - int(sec)) * 1000)
|
||||
return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
|
||||
|
||||
|
||||
def write_clip_srt(srt_path, subtitles, cover_duration):
|
||||
"""写出用于烧录的 SRT(仅保留封面结束后的字幕,时间已相对片段)"""
|
||||
lines = []
|
||||
idx = 1
|
||||
for sub in subtitles:
|
||||
start, end = sub['start'], sub['end']
|
||||
if end <= cover_duration:
|
||||
continue
|
||||
start = max(start, cover_duration)
|
||||
text = (sub.get('text') or '').strip().replace('\n', ' ')
|
||||
if not text:
|
||||
continue
|
||||
lines.append(str(idx))
|
||||
lines.append(f"{_sec_to_srt_time(start)} --> {_sec_to_srt_time(end)}")
|
||||
lines.append(text)
|
||||
lines.append("")
|
||||
idx += 1
|
||||
if not lines:
|
||||
return None
|
||||
with open(srt_path, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(lines))
|
||||
return srt_path
|
||||
|
||||
|
||||
def _is_mostly_chinese(text):
|
||||
if not text or not isinstance(text, str):
|
||||
return False
|
||||
@@ -350,14 +406,14 @@ def get_cover_font(size):
|
||||
return ImageFont.load_default()
|
||||
|
||||
|
||||
def _draw_vertical_gradient(draw, width, height, top_rgb, bottom_rgb):
|
||||
"""绘制竖屏封面用深色渐变背景,高级感"""
|
||||
def _draw_vertical_gradient(draw, width, height, top_rgb, bottom_rgb, alpha=255):
|
||||
"""绘制竖屏封面用深色渐变背景;alpha<255 时为半透明质感"""
|
||||
for y in range(height):
|
||||
t = y / max(height - 1, 1)
|
||||
r = int(top_rgb[0] + (bottom_rgb[0] - top_rgb[0]) * t)
|
||||
g = int(top_rgb[1] + (bottom_rgb[1] - top_rgb[1]) * t)
|
||||
b = int(top_rgb[2] + (bottom_rgb[2] - top_rgb[2]) * t)
|
||||
draw.rectangle([0, y, width, y + 1], fill=(r, g, b))
|
||||
draw.rectangle([0, y, width, y + 1], fill=(r, g, b, alpha))
|
||||
|
||||
|
||||
def create_cover_image(hook_text, width, height, output_path, video_path=None):
|
||||
@@ -370,13 +426,12 @@ def create_cover_image(hook_text, width, height, output_path, video_path=None):
|
||||
is_vertical = (width, height) == (VERTICAL_W, VERTICAL_H)
|
||||
|
||||
if is_vertical:
|
||||
# 竖屏成片:高级深色渐变背景,不依赖视频帧,保证不超出界面
|
||||
img = Image.new('RGB', (width, height), VERTICAL_COVER_TOP)
|
||||
# 竖屏成片:半透明质感封面,渐变背景带 alpha,透出底层画面
|
||||
img = Image.new('RGBA', (width, height), (*VERTICAL_COVER_TOP, VERTICAL_COVER_ALPHA))
|
||||
draw = ImageDraw.Draw(img)
|
||||
_draw_vertical_gradient(draw, width, height, VERTICAL_COVER_TOP, VERTICAL_COVER_BOTTOM)
|
||||
# 轻微半透明暗角,让文字更突出
|
||||
overlay = Image.new('RGBA', (width, height), (0, 0, 0, 100))
|
||||
img = img.convert('RGBA')
|
||||
_draw_vertical_gradient(draw, width, height, VERTICAL_COVER_TOP, VERTICAL_COVER_BOTTOM, alpha=VERTICAL_COVER_ALPHA)
|
||||
# 轻微半透明暗角,不盖死
|
||||
overlay = Image.new('RGBA', (width, height), (0, 0, 0, 60))
|
||||
img = Image.alpha_composite(img, overlay)
|
||||
draw = ImageDraw.Draw(img)
|
||||
else:
|
||||
@@ -687,7 +742,7 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa
|
||||
create_cover_image(hook_text, out_w, out_h, cover_img, clip_path)
|
||||
print(f" ✓ 封面生成")
|
||||
|
||||
# 2. 字幕逻辑:有字幕/图片则跳过,无则烧录中文
|
||||
# 2. 字幕逻辑:有字幕则烧录(图像 overlay:每张图 -loop 1 才能按时间 enable 显示)
|
||||
sub_images = []
|
||||
do_burn_subs = not skip_subs and (force_burn_subs or not detect_burned_subs(clip_path))
|
||||
if skip_subs:
|
||||
@@ -709,8 +764,10 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa
|
||||
for sub in subtitles:
|
||||
if not _is_mostly_chinese(sub['text']):
|
||||
sub['text'] = _translate_to_chinese(sub['text']) or sub['text']
|
||||
print(f" ✓ 字幕解析 ({len(subtitles)}条),已转中文")
|
||||
for i, sub in enumerate(subtitles[:50]):
|
||||
# 仅过滤整句为规则/模板的条目,保留所有对白(含重复句,保证字幕连续)
|
||||
subtitles = _filter_relevant_subtitles(subtitles)
|
||||
print(f" ✓ 字幕解析 ({len(subtitles)}条)")
|
||||
for i, sub in enumerate(subtitles):
|
||||
img_path = os.path.join(temp_dir, f'sub_{i:04d}.png')
|
||||
create_subtitle_image(sub['text'], out_w, out_h, img_path)
|
||||
sub_images.append({'path': img_path, 'start': sub['start'], 'end': sub['end']})
|
||||
@@ -740,22 +797,19 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa
|
||||
current_video = cover_output
|
||||
print(f" ✓ 封面烧录")
|
||||
|
||||
# 5.2 分批烧录字幕(封面结束后才显示,不盖住封面)
|
||||
# 5.2 烧录字幕(图像 overlay;每张图 -loop 1 才能按 enable=between(t,a,b) 显示)
|
||||
if sub_images:
|
||||
batch_size = 8
|
||||
batch_size = 5
|
||||
for batch_idx in range(0, len(sub_images), batch_size):
|
||||
batch = sub_images[batch_idx:batch_idx + batch_size]
|
||||
|
||||
inputs = ['-i', current_video]
|
||||
for img in batch:
|
||||
inputs.extend(['-i', img['path']])
|
||||
|
||||
inputs.extend(['-loop', '1', '-i', img['path']])
|
||||
filters = []
|
||||
last_output = '0:v'
|
||||
for i, img in enumerate(batch):
|
||||
input_idx = i + 1
|
||||
output_name = f'v{i}'
|
||||
# 封面结束后才显示字幕,不盖住封面
|
||||
sub_start = max(img['start'], cover_duration)
|
||||
if sub_start < img['end']:
|
||||
enable = f"between(t,{sub_start:.3f},{img['end']:.3f})"
|
||||
@@ -763,22 +817,20 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa
|
||||
else:
|
||||
filters.append(f"[{last_output}]copy[{output_name}]")
|
||||
last_output = output_name
|
||||
|
||||
filter_complex = ';'.join(filters)
|
||||
batch_output = os.path.join(temp_dir, f'sub_batch_{batch_idx}.mp4')
|
||||
|
||||
cmd = [
|
||||
'ffmpeg', '-y', *inputs,
|
||||
'-filter_complex', filter_complex,
|
||||
'-map', f'[{last_output}]', '-map', '0:a',
|
||||
'-c:v', 'libx264', '-preset', 'fast', '-crf', '22',
|
||||
'-c:a', 'copy', batch_output
|
||||
'-c:a', 'copy', '-shortest', batch_output
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True)
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f" ⚠ 字幕批次 {batch_idx} 报错: {(result.stderr or '')[-500:]}", file=sys.stderr)
|
||||
if result.returncode == 0 and os.path.exists(batch_output):
|
||||
current_video = batch_output
|
||||
|
||||
print(f" ✓ 字幕烧录")
|
||||
|
||||
# 5.3 加速10% + 音频同步
|
||||
|
||||
@@ -1,16 +1,39 @@
|
||||
---
|
||||
name: 读书笔记
|
||||
description: 五行结构化读书笔记。触发词:拆解这本书、写入读书笔记、读书笔记、五行拆书、XMind笔记、卡读、金水木火土。使用金水木火土五行框架拆解书籍,自动写入XMind。包含完整的拆书提示词、XMind写入脚本、操作流程。
|
||||
description: 五行结构化读书笔记。触发词:拆解这本书、写入读书笔记、读书笔记、五行拆书、XMind笔记、卡读、金水木火土。使用金水木火土五行框架拆解书籍,自动写入XMind;本地默认存 2、我写的日记/读书笔记,写完后可同步发飞书知识库对应子目录。
|
||||
group: 火
|
||||
triggers: 拆解这本书、五行拆书、读书笔记
|
||||
triggers: 拆解这本书、五行拆书、读书笔记、读书笔记发飞书
|
||||
owner: 火炬
|
||||
version: "1.0"
|
||||
updated: "2026-02-16"
|
||||
version: "1.1"
|
||||
updated: "2026-03-03"
|
||||
---
|
||||
|
||||
# 读书笔记
|
||||
|
||||
为卡若提供五行结构化读书笔记服务,支持自动写入 XMind。
|
||||
为卡若提供五行结构化读书笔记服务,支持自动写入 XMind;**本地固定存放 + 可选飞书同步**。
|
||||
|
||||
## 默认存放路径(强制)
|
||||
|
||||
- **本地**:`/Users/karuo/Documents/个人/2、我写的日记/读书笔记/`
|
||||
- 文件名:`作者或主题_书名或简称_读书笔记.md`(例:`曾仕强_易经_读书笔记.md`、`Dan_Koe_如何在一天内彻底改造你的人生_读书笔记.md`)
|
||||
- 格式与规则见:`运营中枢/参考资料/卡若读书笔记格式_固定规则.md`
|
||||
|
||||
## 飞书同步(写完后发到飞书链接下子目录)
|
||||
|
||||
- **原则**:读书笔记写完后,**同时发到飞书知识库**指定链接下的**对应子目录**,与本地「2、我写的日记/读书笔记」一致。
|
||||
- **链接与目录**:飞书读书笔记父节点(链接)与子目录规则见下方「飞书读书笔记配置」;先分析该链接对应的 Wiki 节点与目录结构,再按**分类或单目录**落到对应子目录。
|
||||
- **分类与子目录对应**:与主导图分类一致——个人提升 / 人际关系 / 创业 / 商业思维 / 投资;发飞书时可按该分类建子目录(如 `读书笔记/创业/纳瓦尔访谈_读书笔记`),或统一放在 `读书笔记/` 单目录下。
|
||||
- **执行**:使用 `02_卡人(水)/水桥_平台对接/飞书管理/脚本/` 下的统一文章上传或 md→blocks 脚本,`--parent` 取飞书读书笔记父节点 token,标题与本地文件名一致,内容为笔记正文;若需按分类分子目录,则先确保飞书侧存在对应子节点再在该节点下创建文档。
|
||||
|
||||
### 飞书读书笔记配置(链接与子目录)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|:---|:---|
|
||||
| **飞书知识库链接** | 读书笔记要发到的**父节点**对应链接,格式:`https://cunkebao.feishu.cn/wiki/<node_token>`。若尚未确定,请在飞书知识库中新建或选中「读书笔记」节点,复制浏览器地址栏 `wiki/` 后的 token,填入下方或环境变量 `FEISHU_READING_WIKI_PARENT`。 |
|
||||
| **父节点 token** | 链接中 `wiki/` 后的一段,即发书时的 `--parent`。示例:`KNf7wA8Rki1NSdkkSIqcdFtTnWb`(日记/新研究);若读书笔记单独建了节点,用该节点 token。 |
|
||||
| **子目录结构** | 可选:在父节点下建子目录「读书笔记」,再其下按分类建「个人提升」「创业」「商业思维」「投资」「人际关系」,与主导图分类一致;或父节点下直接为「读书笔记」单目录,所有笔记均发至此。 |
|
||||
|
||||
**未配置时**:若暂无飞书链接,笔记仍只写本地 `2、我写的日记/读书笔记/`;配置好父节点后,写新笔记时再同时发飞书并落到对应子目录。
|
||||
|
||||
## XMind 文件
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
- **图片与流程图**:笔记内引用的图片、流程图建议与笔记**同目录**存放,用 `文件名.png` 或 `./文件名.png` 引用,避免用 `../../图片/` 等跨目录路径导致在不同预览/工作区下**不显示**。若用 Mermaid,可在同节内再插入一张导出好的流程图 PNG,保证不依赖渲染也能看到图。
|
||||
- **图裂了怎么办**:若在 Cursor/VS Code 里图片不显示,可(1)用 Typora 打开该 md(以文件所在目录为基准);(2)在 Finder 中打开笔记所在文件夹,双击 md 用系统默认预览打开;(3)直接打开同目录下对应的 PNG 文件。建议在每张图下方加一句备用说明:「若图不显示,请打开本笔记所在文件夹中的 `xxx.png` 查看。」
|
||||
- **记忆写入**:在 `个人/1、卡若:本人/记忆.md` 当日日期下追加 1~2 行摘要 + 本笔记文件路径;便于后续从记忆调出。
|
||||
- **飞书同步**:读书笔记写完后,**同时发到飞书知识库**指定链接下的**对应子目录**。链接即飞书知识库「读书笔记」所在节点的地址(`https://cunkebao.feishu.cn/wiki/<node_token>`),子目录可与五行分类对应(个人提升/创业/商业思维/投资/人际关系)或统一为「读书笔记」单目录。父节点 token 与脚本用法见读书笔记 Skill「飞书同步」与「飞书读书笔记配置」。
|
||||
|
||||
---
|
||||
|
||||
|
||||
50
运营中枢/工作台/2026-03-03_综合复盘_大目标与本月完成度.md
Normal file
50
运营中枢/工作台/2026-03-03_综合复盘_大目标与本月完成度.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# 2026-03-03 综合复盘:大目标与本月完成度
|
||||
|
||||
> 依据本机今日相关内容、2026 年整体目标、当前任务看板与昨日未完成项综合分析与思考,并更新完成比例。
|
||||
|
||||
---
|
||||
|
||||
**[卡若复盘](2026-03-03)**
|
||||
|
||||
**🎯 目标·结果·达成率**
|
||||
大目标(全年 100%)与本月小目标(约 12%)对齐;昨日未完成已叠加今日;完成比例已按本机内容更新。达成率按项区分见下。
|
||||
|
||||
**📌 过程**
|
||||
|
||||
1. **本机今日与目标相关内容**
|
||||
- 已读:`2026年整体目标.md`(总目标 100%、本月 % = 当前相对总目标完成度)、`当前任务看板.md`(T003 卡若AI 4 项优化、T004 一人公司 5%、T005 玩值电竞 25%)、`2026年2月_整体月复盘.md`(2 月主线 12%、一人公司/玩值电竞/今日核心延续)。
|
||||
- 今日相关:3 月飞书日志(3月3日)已写「昨日目标与总目标一致、本月 12% 距最终 88%」;视频 Skill(四屏切片)、20 条 Soul、20:00 朋友圈为当日执行项。
|
||||
|
||||
2. **大目标 vs 本月小事**
|
||||
- **大目标**:全年终态 100%;当前相对总目标完成度 **约 12%**,距最终差 **88%**。
|
||||
- **本月小事**:一人公司 Agent 5%;玩值电竞 25%;卡若AI 4 项优化执行中;每日 20 条 Soul 视频 + 20:00 发 1 条朋友圈;视频 Skill 四屏切片完成 20 个/日;飞书日志每日迭代。大目标统摄本月小事,百分比均相对总目标。
|
||||
|
||||
3. **昨日未完成叠加今日**
|
||||
- 昨日(3月2日)若未打满:**20 条 Soul 视频**、**20:00 发 1 条朋友圈**、**四屏切片 20 条**、**飞书日志** → 今日继续执行并计入当日完成度。
|
||||
- 看板未闭环项(T003/T004/T005)持续执行中,今日仍计入一人公司、玩值电竞、卡若AI 优化相关动作。
|
||||
|
||||
4. **完成比例更新(按本机与目标一致性)**
|
||||
|
||||
- **相对总目标**:本月目标 **12%**,距最终 **88%**(维持,与 2026年整体目标 一致)。
|
||||
- **T004 一人公司**:**5%**(看板已载明,维持)。
|
||||
- **T005 玩值电竞**:**25%**(看板已载明,维持)。
|
||||
- **今日核心**:20 条 Soul + 1 条朋友圈 + 四屏切片 20 条 → 当日完成度建议在飞书 F(反馈) 中填「四屏切片 20 条→X%」(X = 当日完成数/20×100 或按实际条数折算)。
|
||||
- **飞书日志**:3月3日已写,迭代 100%。
|
||||
|
||||
**💡 反思**
|
||||
|
||||
1. **大目标与本月**:总目标 100% 为锚点,本月 12% 与一人公司 5%、玩值电竞 25% 等均为相对总目标的进度,避免本月小事与全年脱节。
|
||||
2. **昨日未完成叠加**:每日 20 条视频、1 条朋友圈、四屏切片 20 条若昨日未满额,今日应叠加执行并在反馈中更新当日完成度,便于看趋势。
|
||||
3. **本机内容与目标**:工作台与看板、2026年整体目标、2 月整体月复盘、飞书 3 月日志形成「总目标→本月→每日」链条,复盘时先读整体目标再更新比例,保证一致性。
|
||||
|
||||
**📝 总结**
|
||||
|
||||
综合本机今日相关文档与目标:大目标(100%)与本月(12%)、本月小事(一人公司 5%、玩值电竞 25%、今日核心 20 条+1 朋友圈+四屏切片 20 条)已对齐;昨日未完成项已叠加到今日计划;完成比例已按上述更新,飞书 3月3日 日志中可填「四屏切片 20 条→当日%」。
|
||||
|
||||
**▶ 下一步执行**
|
||||
|
||||
今日执行:20 条 Soul 视频(四屏切片)、20:00 发 1 条朋友圈、视频 Skill 操作、飞书日志;晚些在飞书 3月3日 F(反馈) 补「四屏切片 20 条→X%」。明日写日志前先读《2026年整体目标》,昨日未完成继续叠加。看板 T003/T004/T005 按进度更新上下文摘要与完成度。
|
||||
|
||||
---
|
||||
|
||||
*依据:运营中枢/工作台(当前任务看板、2026年整体目标、2月整体月复盘)、飞书 3月3日 日志设计、昨日未完成叠加规则。*
|
||||
@@ -212,3 +212,4 @@
|
||||
| 2026-03-02 20:52:08 | 🔄 卡若AI 同步 2026-03-02 20:52 | 更新:金盾、卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 14 个 |
|
||||
| 2026-03-03 04:51:53 | 🔄 卡若AI 同步 2026-03-03 04:51 | 更新:金盾、运营中枢工作台 | 排除 >20MB: 14 个 |
|
||||
| 2026-03-03 04:58:10 | 🔄 卡若AI 同步 2026-03-03 04:58 | 更新:Cursor规则、总索引与入口、运营中枢工作台 | 排除 >20MB: 14 个 |
|
||||
| 2026-03-03 05:02:46 | 🔄 卡若AI 同步 2026-03-03 05:02 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 14 个 |
|
||||
|
||||
@@ -215,3 +215,4 @@
|
||||
| 2026-03-02 20:52:08 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-02 20:52 | 更新:金盾、卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-03-03 04:51:53 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-03 04:51 | 更新:金盾、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-03-03 04:58:10 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-03 04:58 | 更新:Cursor规则、总索引与入口、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-03-03 05:02:46 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-03 05:02 | 更新:水桥平台对接、运营中枢工作台 | 排除 >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