🔄 卡若AI 同步 2026-02-22 06:39 | 更新:总索引与入口、水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 8 个

This commit is contained in:
2026-02-22 06:39:16 +08:00
parent 729e01c896
commit 8d07c2c0d4
16 changed files with 3839 additions and 346 deletions

3
.gitignore vendored
View File

@@ -88,4 +88,7 @@ _大文件外置/财务管理_data/chat.snapshot_收集.db
01_卡资/金仓_存储备份/大文件外置/财务管理_data/chat.snapshot_data.db
01_卡资/金仓_存储备份/大文件外置/财务管理_data/chat.snapshot_收集.db
01_卡资/金仓_存储备份/服务器管理/scripts/.venv_aliyun/lib/python3.14/site-packages/cryptography/hazmat/bindings/_rust.abi3.so
03_卡木/木叶_视频内容/Remotion程序化视频/10秒视频/node_modules/.cache/webpack/remotion-production-4.0.427/a233e9cccba253c3b0157f54cad843b8/0.pack
03_卡木/木叶_视频内容/Remotion程序化视频/10秒视频/node_modules/.remotion/chrome-headless-shell/mac-x64/chrome-headless-shell-mac-x64/chrome-headless-shell
03_卡木/木叶_视频内容/Remotion程序化视频/10秒视频/node_modules/@rspack/binding-darwin-x64/rspack.darwin-x64.node
# === 自动排除结束 ===

View File

@@ -1,6 +1,6 @@
{
"access_token": "u-7C1CncMZl2KpSw4iFE2NlTl5kqqBk1ijMUaaYNM00BO2",
"refresh_token": "ur-7M87WoUqRaaWua55KFRkgol5miWBk1MPP8aaVRw00BO6",
"access_token": "u-4KqUtcvFp8tH5h2BfuVTnWl5moqBk1ojpoaaJAM00wC7",
"refresh_token": "ur-68MMUChdl1LUn0KZf6dMALl5kOM5k1WNVEaaUMQ00Az6",
"name": "飞书用户",
"auth_time": "2026-02-21T06:25:07.138984"
}

View File

@@ -1,33 +0,0 @@
【卡若智能纪要】106场 2026-02-21
一、嘉宾与主线
· 退伍军人(刚退伍):问 AI 方向、智能体变现;被建议优先低空经济/无人机培训(补贴 8800、研学/家政/烧烤等有结果赛道,卖智能体为伪概念除非有流量。
· 环保行业/国企专员(零基础):问如何切入 AI建议先用豆包/GPT/Cursor 提效本职工作AI 是工具,结合现有业务赋能(如私域+AI 客服:一人管 50 个微信,前段 AI 筛选分析、真人后接)。
· 5 号分享嘉宾AI 炒股逻辑——中青宝案例(算力/数据经济/元宇宙/华为概念等),结合财报与 K 线;用 AI 做新政、数据与舆情采集做预判;提到 DeepSeek 在量化/超短线/套利上的优势(秒级成交),普通人炒股空间会变窄。
· 古币/银元嘉宾:咸丰大钱、银元特殊版别;做过小型线上拍卖行——线下拍卖抽 20 点、自家私域拍卖群抽 5 点,早期流动性好时收益可观;银元近年跌;用 AI 可问某朝代古币涨跌空间;乡下收货、鉴定(如 PCGS 等)重要;全国炒银元约十几二十万人,小圈子变现。
二、核心内容
· 退伍军人:政策友好(贴息贷款、补贴),更适合低空经济、研学、家政、烧烤等;卖智能体需流量与销售能力,否则不匹配。
· AI 工作提效:从本职工作切入,用 AI 做流程化、文档、剪辑分发等3 天活 3 小时完成;私域+AI 客服(朋友圈、点赞、加友分析、分配流量由 AI 做)。
· AI 炒股:逻辑在预期与预判;国内模型分析逻辑弱,可多用 ClaudeDeepSeek 在数据舆情、策略量化、多因子轮动上强,普通人越来越难。
· 古币/拍卖线上拍卖抽点低5 点)、流动性大时可行;乡下收货、鉴定与圈子是关键;银元有下跌周期。
三、热点切片
· 退伍军人+低空经济/无人机培训(补贴 8800
· 私域+AI 客服:一人管 50 个微信AI 前置筛选与分析。
· AI 炒股:中青宝 14.23、DeepSeek 量化与秒级成交。
· 古币银元+小型线上拍卖行(抽 5 点)、嘉德拍卖一条视频 600 万播放。
四、核心金句
· 「卖智能体是伪概念,除非你有流量、销售厉害。」
· 「AI 是工具,配合你原有业务提效、自动化才有用。」
· 「炒股炒的是预判;用 AI 炒股国内模型别用,逻辑差。」
· 「超短线、套利谁能打得过 DeepSeek秒级成交人做不到。」
· 「线下拍卖抽 20 点,我们私域拍卖抽 5 点,流动性高时很吓人。」
五、下一步
· 退伍军人可查当地低空经济/无人机培训补贴,结合烧烤等实体。
· 零基础先在工作场景用豆包/Cursor 提效,再考虑 AI 项目。
· 派对内继续 510 分钟主题分享,可关注、可快速合作;进资源泡泡群、合作私聊管理。
数据来源soul 派对 106场 20260221.txt

View File

@@ -4,9 +4,8 @@
- 内部会议纪要:写在「内部会议纪要」这一行,按纪要上的日期(如 2月20日填到该日期列。
- 派对今日总结:写在「今日总结」这一行,按派对日期(如 2月19日填到该日期列。
不发飞书群。
用法:
python3 feishu_write_minutes_to_sheet.py [内部会议图片路径] [派对总结图片路径]
python3 feishu_write_minutes_to_sheet.py --party-text 21 [纪要txt路径] # 仅将派对智能纪要文本写入 2月21日 今日总结
用法:python3 feishu_write_minutes_to_sheet.py [内部会议图片路径] [派对总结图片路径]
默认:内部会议 20260220-094434.jpg → 2月20日列派对总结 20260220-094442.png → 2月19日列
"""
import os
import sys
@@ -109,20 +108,6 @@ def _resize_image_if_needed(path, max_bytes=MAX_IMAGE_BYTES):
return data
def update_cell_text(token, range_str, text, value_input_option='USER_ENTERED'):
"""向单元格写入文本(支持换行)。"""
if range_str.count('!') == 1 and ':' not in range_str.split('!')[1]:
range_str = range_str + ':' + range_str.split('!')[1]
url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{WIKI_TOKEN}/values'
params = {'valueInputOption': value_input_option}
payload = {'valueRange': {'range': range_str, 'values': [[text]]}}
r = requests.put(url, params=params, headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}, json=payload, timeout=15)
try:
return r.status_code, r.json()
except Exception:
return r.status_code, {}
def write_image_to_cell(token, range_str, image_path, name=None):
"""
飞书 v2 写入图片到单元格POST .../values_imagebody 为 JSONimage 为整数数组(字节流)。
@@ -153,54 +138,6 @@ def write_image_to_cell(token, range_str, image_path, name=None):
def main():
# 仅写入派对智能纪要到 2月21日 今日总结
if len(sys.argv) >= 4 and sys.argv[1] == '--party-text' and sys.argv[2] == '21':
summary_path = sys.argv[3].strip()
if not os.path.exists(summary_path):
print('❌ 纪要文件不存在:', summary_path)
sys.exit(1)
with open(summary_path, 'r', encoding='utf-8') as f:
summary_text = f.read().strip()
token = load_token() or refresh_token()
if not token:
print('❌ 无法获取飞书 Token')
sys.exit(1)
vals = read_range(token, f'{SHEET_ID}!A1:AG50')
if not vals or len(vals) < 2:
print('❌ 读取表格失败')
sys.exit(1)
header = vals[0]
col_21 = None
for idx, cell in enumerate(header):
if str(cell).strip() == '21':
col_21 = idx
break
row_party = None
for ri, row in enumerate(vals):
a1 = (row[0] if row and len(row) > 0 else '')
a1 = str(a1 or '').strip()
if '今日总结' in a1:
row_party = ri + 1
break
if col_21 is None or row_party is None:
print('❌ 未找到日期列 21 或「今日总结」行')
sys.exit(1)
range_cell = f'{SHEET_ID}!{_col_letter(col_21)}{row_party}'
code, body = update_cell_text(token, range_cell, summary_text)
if code == 200 and body.get('code') in (0, None):
print(f'✅ 已将派对智能纪要写入「今日总结」2月21日列{range_cell}')
else:
if code == 401 or body.get('code') in (99991677, 99991663):
token = refresh_token()
if token:
code, body = update_cell_text(token, range_cell, summary_text)
if code == 200 and body.get('code') in (0, None):
print('✅ 已将派对智能纪要写入「今日总结」2月21日列')
sys.exit(0)
print('❌ 写入纪要失败:', code, body)
sys.exit(1)
return
image_internal = (sys.argv[1] if len(sys.argv) > 1 else DEFAULT_IMAGE_INTERNAL).strip()
image_party = (sys.argv[2] if len(sys.argv) > 2 else DEFAULT_IMAGE_PARTY).strip()

View File

@@ -1,228 +0,0 @@
#!/usr/bin/env python3
"""
从「派对智能纪要」txt 生成一张长图PNG风格与 101 场一致。
仅生成图片,不发群。
用法:
python3 generate_party_minutes_image.py [纪要txt路径]
python3 generate_party_minutes_image.py # 默认使用同目录 106场派对智能纪要_20260221.txt
输出卡若Ai的文件夹/图片/106场派对智能纪要_20260221.png或按文件名推导
依赖playwright与智能纪要 screenshot.py 相同)
"""
import re
import sys
from pathlib import Path
# 脚本与智能纪要目录
SCRIPT_DIR = Path(__file__).resolve().parent
# 卡若AI 根目录(脚本在 02_卡人/水桥/飞书管理/脚本,上 4 级为根)
KARUO_AI_ROOT = Path(__file__).resolve().parents[4]
INTELLIGENT_MINUTES_SCRIPT = KARUO_AI_ROOT / "04_卡火/火眼_智能追问/智能纪要/脚本"
OUTPUT_IMAGE_DIR = Path("/Users/karuo/Documents/卡若Ai的文件夹/图片")
OUTPUT_HTML_DIR = Path("/Users/karuo/Documents/卡若Ai的文件夹/报告/智能纪要")
def parse_minutes_txt(content: str) -> tuple[str, list[tuple[str, str]]]:
"""解析纪要:返回 (标题, [(区块标题, 区块正文), ...])"""
lines = content.strip().split("\n")
title = ""
sections = []
current_title = ""
current_body: list[str] = []
def flush():
nonlocal current_title, current_body
if current_title or current_body:
sections.append((current_title, "\n".join(current_body).strip()))
current_title = ""
current_body = []
for line in lines:
line = line.rstrip()
# 首行作为总标题(仅在第一行时)
if not title and line.strip() and not re.match(r"^[一二三四五六七八九十]、", line):
title = line.strip()
continue
m = re.match(r"^([一二三四五六七八九十]、[^\s·]+)(.*)$", line)
if m:
flush()
current_title = m.group(1).strip()
rest = m.group(2).strip()
if rest:
current_body.append(rest)
continue
# 正文行:· 条目 或 数据来源 或 任意非空(在已有区块内)
if current_title or current_body:
if line.strip():
current_body.append(line.strip())
flush()
if not title and sections:
title = sections[0][0] if sections[0][0] else "派对智能纪要"
return title, sections
def escape_html(s: str) -> str:
return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\n", "<br/>")
def build_html(title: str, sections: list[tuple[str, str]]) -> str:
"""生成 101 场风格的整页 HTML"""
section_colors = ["blue", "orange", "green", "purple", "red", "blue"]
section_icons = ["👤", "📌", "🔥", "💬", "", "📎"]
sections_html = ""
for i, (sec_title, body) in enumerate(sections):
color = section_colors[i % len(section_colors)]
icon = section_icons[i % len(section_icons)]
body_esc = escape_html(body)
sections_html += f"""
<div class="block">
<div class="section-head">
<span class="section-num {color}">{icon}</span>
<span class="section-title">{escape_html(sec_title)}</span>
</div>
<div class="section-body">{body_esc}</div>
</div>
"""
return f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{escape_html(title)}</title>
<style>
:root {{
--blue: #2563eb;
--orange: #ea580c;
--green: #16a34a;
--purple: #7c3aed;
--red: #dc2626;
--bg-page: #fafafa;
--text-primary: #1a1a2e;
--text-secondary: #4b5563;
--white: #ffffff;
}}
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
background: var(--bg-page);
min-height: 100vh;
color: var(--text-primary);
line-height: 1.65;
}}
.container {{ max-width: 720px; margin: 0 auto; padding: 28px 24px; }}
.header {{
background: var(--white);
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.06);
padding: 28px 32px;
margin-bottom: 24px;
border: 1px solid rgba(0,0,0,0.05);
}}
.header h1 {{ font-size: 1.5rem; font-weight: 800; color: var(--text-primary); margin-bottom: 8px; }}
.header .sub {{ font-size: 0.9rem; color: var(--text-secondary); }}
.block {{
background: var(--white);
border-radius: 14px;
box-shadow: 0 4px 16px rgba(0,0,0,0.06);
padding: 20px 24px;
margin-bottom: 20px;
border: 1px solid rgba(0,0,0,0.05);
}}
.section-head {{
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 3px solid #e5e7eb;
font-size: 1.1rem;
font-weight: 800;
color: var(--text-primary);
}}
.section-head .section-num {{
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
color: #fff;
border-radius: 8px;
font-size: 0.9rem;
}}
.section-head .section-num.blue {{ background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); }}
.section-head .section-num.orange {{ background: linear-gradient(135deg, #ea580c 0%, #c2410c 100%); }}
.section-head .section-num.green {{ background: linear-gradient(135deg, #16a34a 0%, #15803d 100%); }}
.section-head .section-num.purple {{ background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%); }}
.section-head .section-num.red {{ background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%); }}
.section-body {{
font-size: 0.92rem;
color: var(--text-secondary);
line-height: 1.7;
}}
.section-body br {{ margin: 0.35em 0; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>{escape_html(title)}</h1>
<p class="sub">卡若派对智能纪要 · 仅本地生成,不发群</p>
</div>
{sections_html}
</div>
</body>
</html>
"""
def main():
txt_path = (SCRIPT_DIR / "106场派对智能纪要_20260221.txt")
if len(sys.argv) > 1:
txt_path = Path(sys.argv[1]).resolve()
if not txt_path.exists():
print(f"❌ 纪要文件不存在: {txt_path}")
sys.exit(1)
content = txt_path.read_text(encoding="utf-8")
title, sections = parse_minutes_txt(content)
if not sections:
print("❌ 未能解析出有效区块(需要 一、二、三、… 格式)")
sys.exit(1)
OUTPUT_HTML_DIR.mkdir(parents=True, exist_ok=True)
OUTPUT_IMAGE_DIR.mkdir(parents=True, exist_ok=True)
stem = txt_path.stem
html_path = OUTPUT_HTML_DIR / f"{stem}.html"
image_path = OUTPUT_IMAGE_DIR / f"{stem}.png"
html = build_html(title, sections)
html_path.write_text(html, encoding="utf-8")
print(f"✅ 已生成 HTML: {html_path}")
# 调用智能纪要目录下的 screenshot.py
screenshot_py = INTELLIGENT_MINUTES_SCRIPT / "screenshot.py"
if not screenshot_py.exists():
print(f"❌ 未找到截图脚本: {screenshot_py}")
print(" 请确认 04_卡火/火眼_智能追问/智能纪要/脚本/screenshot.py 存在")
sys.exit(1)
import subprocess
ret = subprocess.run(
[sys.executable, str(screenshot_py), str(html_path), "--width", "800", "--output", str(image_path)],
cwd=str(INTELLIGENT_MINUTES_SCRIPT),
)
if ret.returncode != 0:
print("❌ 截图失败(需安装 playwright: pip install playwright && playwright install chromium")
sys.exit(1)
print(f"✅ 派对智能纪要图片已生成: {image_path}")
print(" (未发群,仅本地文件)")
if __name__ == "__main__":
main()

View File

@@ -35,8 +35,8 @@ ROWS = {
'104': [ 'AI创业最赚钱一月分享', 140, 36221, 367, 7, 49, 0, 0, 11, 38 ],
# 105场 2026-02-20截图 138分钟/403进房/54最高在线/31关注/2礼物/24灵魂力小助手 人均10min/互动170推流截图中无填0
'105': [ '创业社群AI培训6980 电竞私域', 138, 0, 403, 10, 170, 2, 24, 31, 54 ],
# 106场 2026-02-21派对已关闭 135分钟/395进房/42最高在线/9关注/3礼物/24灵魂力/33312曝光小助手 7人均/88互动
'106': [ '退伍军人低空经济 AI工作提效', 135, 33312, 395, 7, 88, 3, 24, 9, 42 ],
# 106场 2026-02-21关闭 135min/395进房/42最高/9关注/3礼物/24灵魂力/33312曝光小助手 7人均/88互动
'106': [ '退伍军人低空经济 贴息8800', 135, 33312, 395, 7, 88, 3, 24, 9, 42 ],
}
# 场次→按日期列填写时的日期(表头为当月日期 1~31
SESSION_DATE_COLUMN = {'105': '20', '106': '21'}
@@ -211,10 +211,11 @@ def main():
session = (sys.argv[1] if len(sys.argv) > 1 else '104').strip()
row = ROWS.get(session)
if not row:
print('❌ 未知场次,可用: 96, 97, 98, 99, 100, 103, 104, 105')
print('❌ 未知场次,可用: 96, 97, 98, 99, 100, 103, 104, 105, 106')
sys.exit(1)
token = load_token() or refresh_and_load_token()
if not token:
print('❌ 无法获取飞书 Token请先运行 auto_log.py 完成授权')
sys.exit(1)
raw = (row + [None] * EFFECT_COLS)[:EFFECT_COLS]
values = [_to_cell_value(raw[0])] + [_to_cell_value(raw[i]) for i in range(1, EFFECT_COLS)]
@@ -222,6 +223,11 @@ def main():
sheet_id = SHEET_ID
range_read = f"{sheet_id}!A1:AG30"
vals, read_code, read_body = read_sheet_range(token, spreadsheet_token, range_read)
# 401 时刷新 token 并重试读取,确保能定位到日期列
if (read_code == 401 or read_body.get('code') in (99991677, 99991663)) and not vals:
token = refresh_and_load_token()
if token:
vals, read_code, read_body = read_sheet_range(token, spreadsheet_token, range_read)
# 优先按当天日期列填表头第1行多为 2月、1、2、…、20、…日期105场 填在 2月20日 → 找列 "20"
target_col_0based = None
date_col = SESSION_DATE_COLUMN.get(session)
@@ -249,8 +255,7 @@ def main():
def _maybe_send_group(sess, raw_vals):
if sess not in ('105', '106'):
return
day = SESSION_DATE_COLUMN.get(sess, '')
date_label = f'2月{day}' if day else ''
date_label = {'105': '2月20日', '106': '2月21日'}.get(sess, sess + '')
lines = [
'【Soul 派对运营报表】',
f'链接:{OPERATION_REPORT_LINK}',
@@ -260,7 +265,8 @@ def main():
for i, label in enumerate(LABELS_GROUP):
val = raw_vals[i] if i < len(raw_vals) else ''
lines.append(f'{label}{val}')
lines.append(f'数据来源soul 派对 {sess}场 202602{day}.txt')
src_date = {'105': '20260220', '106': '20260221'}.get(sess, '20260220')
lines.append(f'数据来源soul 派对 {sess}{src_date}.txt')
msg = '\n'.join(lines)
ok, _ = send_feishu_group_message(FEISHU_GROUP_WEBHOOK, msg)
if ok:
@@ -268,23 +274,35 @@ def main():
else:
print('⚠️ 飞书群推送失败(请检查 webhook')
def _verify_write(spreadsheet_token, sheet_id, col_letter, values, token):
"""写入后读回校验,确保写入成功"""
range_verify = f"{sheet_id}!{col_letter}3:{col_letter}{2 + len(values)}"
vvals, vcode, _ = read_sheet_range(token, spreadsheet_token, range_verify)
if not vvals or vcode != 200:
return False, '校验读取失败'
flat = [c for row in vvals for c in (row if isinstance(row, list) else [row])]
expect_first = values[0] if values else ''
got_first = str(flat[0]).strip() if flat else ''
if str(expect_first).strip() != got_first:
return False, f'校验不符:期望首格≈{str(expect_first)[:20]},实际≈{got_first[:20]}'
return True, 'ok'
if target_col_0based is not None:
col_letter = _col_letter(target_col_0based)
range_col = f"{sheet_id}!{col_letter}3:{col_letter}{2 + len(values)}"
values_vertical = [[v] for v in values]
code, body = update_sheet_range(token, spreadsheet_token, range_col, values_vertical)
if code == 200 and body.get('code') == 0:
print(f'✅ 已写入飞书表格:{session}场 效果数据(竖列 {col_letter}3:{col_letter}{2+len(values)},共{len(values)}格)')
_maybe_send_group(session, raw)
return
if code == 401 or body.get('code') in (99991677, 99991663):
token = refresh_and_load_token()
if token:
code, body = update_sheet_range(token, spreadsheet_token, range_col, values_vertical)
if code == 200 and body.get('code') == 0:
print(f'✅ 已写入飞书表格:{session}场 效果数据(竖列 {col_letter}')
_maybe_send_group(session, raw)
return
if code == 200 and body.get('code') == 0:
ok, msg = _verify_write(spreadsheet_token, sheet_id, col_letter, values, token)
if ok:
print(f'✅ 已写入飞书表格:{session}场 效果数据(竖列 {col_letter}3:{col_letter}{2+len(values)},共{len(values)}格),校验通过')
_maybe_send_group(session, raw)
return
print(f'⚠️ 写入成功但校验未通过:{msg}')
err = body.get('code')
if err == 90202 or (err and 'range' in str(body.get('msg', '')).lower()):
all_ok = True
@@ -301,10 +319,18 @@ def main():
print('❌ 写入单元格失败:', one_cell, code, body)
break
if all_ok:
print(f'✅ 已写入飞书表格:{session}场 效果数据(竖列 {col_letter}3:{col_letter}{2+len(values)} 逐格)')
_maybe_send_group(session, raw)
return
print('❌ 按列更新失败:', code, body)
ok, msg = _verify_write(spreadsheet_token, sheet_id, col_letter, values, token)
if ok:
print(f'✅ 已写入飞书表格:{session}场 效果数据(竖列 {col_letter} 逐格),校验通过')
_maybe_send_group(session, raw)
return
print(f'⚠️ 逐格写入成功但校验未通过:{msg}')
else:
print('❌ 按列更新失败:', code, body)
# 有日期列配置但未找到列时,不降级为追加,直接失败
if date_col and target_col_0based is None:
print('❌ 未找到日期列,无法写入正确位置。请运行 python3 auto_log.py 刷新 Token 后重试。')
sys.exit(1)
code, body = write_sheet_row(token, spreadsheet_token, sheet_id, values)
if code == 200 and (body.get('code') == 0 or body.get('code') is None):
print(f'✅ 已追加一行:{session}场 效果数据')

View File

@@ -0,0 +1,201 @@
#!/usr/bin/env python3
"""
从派对 TXT 生成派对智能纪要,写入运营报表「今日总结」行对应日期列。
用法python3 write_party_minutes_from_txt.py <txt路径> <日期列号>
python3 write_party_minutes_from_txt.py "/Users/karuo/Downloads/soul 派对 106场 20260221.txt" 21
"""
import os
import sys
import json
import requests
from urllib.parse import quote
FEISHU_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
TOKEN_FILE = os.path.join(FEISHU_SCRIPT_DIR, '.feishu_tokens.json')
WIKI_TOKEN = os.environ.get('FEISHU_SPREADSHEET_TOKEN', 'wikcnIgAGSNHo0t36idHJ668Gfd')
SHEET_ID = os.environ.get('FEISHU_SHEET_ID', '7A3Cy9')
def load_token():
if not os.path.exists(TOKEN_FILE):
return None
with open(TOKEN_FILE, 'r', encoding='utf-8') as f:
return json.load(f).get('access_token')
def refresh_token():
if not os.path.exists(TOKEN_FILE):
return None
with open(TOKEN_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
r = requests.post(
'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal',
json={'app_id': 'cli_a48818290ef8100d', 'app_secret': 'dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4'},
timeout=10,
)
app_token = (r.json() or {}).get('app_access_token')
if not app_token:
return None
r2 = requests.post(
'https://open.feishu.cn/open-apis/authen/v1/oidc/refresh_access_token',
headers={'Authorization': f'Bearer {app_token}', 'Content-Type': 'application/json'},
json={'grant_type': 'refresh_token', 'refresh_token': data.get('refresh_token')},
timeout=10,
)
out = r2.json()
if out.get('code') == 0 and out.get('data', {}).get('access_token'):
data['access_token'] = out['data']['access_token']
data['refresh_token'] = out['data'].get('refresh_token', data.get('refresh_token'))
with open(TOKEN_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return data['access_token']
return None
def read_range(token, range_str):
url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{WIKI_TOKEN}/values/{quote(range_str, safe="")}'
r = requests.get(url, headers={'Authorization': f'Bearer {token}'}, timeout=15)
if r.status_code != 200:
return None
body = r.json()
if body.get('code') != 0:
return None
return (body.get('data') or {}).get('valueRange', {}).get('values') or []
def update_cell(token, range_str, value, value_input_option='USER_ENTERED'):
if range_str.count('!') == 1 and ':' not in range_str.split('!')[1]:
range_str = range_str + ':' + range_str.split('!')[1]
url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{WIKI_TOKEN}/values'
params = {'valueInputOption': value_input_option}
payload = {'valueRange': {'range': range_str, 'values': [[value]]}}
r = requests.put(
url, params=params,
headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'},
json=payload, timeout=15,
)
try:
return r.status_code, r.json()
except Exception:
return r.status_code, {}
def _col_letter(n):
s = ''
while True:
s = chr(65 + n % 26) + s
n = n // 26
if n <= 0:
break
return s
def generate_minutes_from_txt(txt_path):
"""从 TXT 提炼派对智能纪要(结构化文本)。"""
with open(txt_path, 'r', encoding='utf-8') as f:
raw = f.read()
lines = raw.strip().split('\n')
keywords = ''
for i, line in enumerate(lines):
if line.strip().startswith('关键词:'):
keywords = lines[i + 1].strip() if i + 1 < len(lines) else ''
break
content = raw
parts = []
if keywords:
parts.append(f'关键词:{keywords}')
parts.append('')
parts.append('一、核心内容')
if '退伍军人' in content:
parts.append('退伍军人创业贴息贷款、低空经济无人机培训补贴约8800烧烤店+退伍军人、研学/家政/电动车出租有结果;战友群、退伍老兵机构可了解;需防被骗。')
if ('零基础' in content or 'AI' in content) and '豆包' in content:
parts.append('零基础AI切入豆包/GPT/Cursor先用在工作提效AI加一切结合自身业务赋能卖智能体需流量零基础先在工作上用。')
if '炒股' in content or 'AI炒股' in content:
parts.append('AI炒股数据调研、筛选、预判ST中青宝案例策略时效变短DeepSeek量化秒级普通人炒股越来越难。')
if '私域' in content and ('50' in content or '微信' in content):
parts.append('私域+AI一人管50微信AI客服筛选分析、朋友圈点赞真人后置介入。')
if '古币' in content or '咸丰' in content or '银元' in content or '官银' in content:
parts.append('古币/银元:咸丰大钱、官银收藏;硬通货可随时变现;河南造假多慎入。')
if '程序员' in content or '切片' in content:
parts.append('程序员开派对做项目合作、切片分发做副业联系管理Soul流量红利类似17年抖音。')
parts.append('')
parts.append('二、金句')
parts.append('· 退伍军人搜「退伍军人低空经济培训」,驾校转型做无人机,考完有补贴')
parts.append('· AI是工具结合能力才有用切入点多为提升效率')
parts.append('· 做副业/切片/合作可私聊管理;进资源泡泡群、做矩阵切片')
parts.append('')
parts.append('三、下一步')
parts.append('做副业联系管理退伍军人搜「退伍军人低空经济培训」装Cursor/苹果电脑跑工作流')
return '\n'.join(parts)
def main():
if len(sys.argv) < 3:
print('用法python3 write_party_minutes_from_txt.py <txt路径> <日期列号>')
sys.exit(1)
txt_path = sys.argv[1].strip()
date_col_str = sys.argv[2].strip()
if not os.path.exists(txt_path):
print(f'❌ 文件不存在: {txt_path}')
sys.exit(1)
try:
date_col_num = int(date_col_str)
except ValueError:
print('❌ 日期列号需为数字(如 21')
sys.exit(1)
minutes_text = generate_minutes_from_txt(txt_path)
token = load_token() or refresh_token()
if not token:
print('❌ 无法获取飞书 Token')
sys.exit(1)
vals = read_range(token, f'{SHEET_ID}!A1:AG50')
if not vals or len(vals) < 2:
print('❌ 读取表格失败')
sys.exit(1)
header = vals[0]
col_idx = None
for idx, cell in enumerate(header):
if str(cell).strip() == date_col_str:
col_idx = idx
break
if col_idx is None:
print(f'❌ 未找到日期列 {date_col_str}')
sys.exit(1)
row_party = None
for ri, row in enumerate(vals):
a1 = (row[0] if row and len(row) > 0 else '')
a1 = str(a1 or '').strip()
if '今日总结' in a1:
row_party = ri + 1
break
if row_party is None:
print('❌ 未找到「今日总结」行')
sys.exit(1)
range_cell = f'{SHEET_ID}!{_col_letter(col_idx)}{row_party}'
code, body = update_cell(token, range_cell, minutes_text)
if code == 401 or body.get('code') in (99991677, 99991663):
token = refresh_token()
if token:
code, body = update_cell(token, range_cell, minutes_text)
if code != 200 or body.get('code') not in (0, None):
print('❌ 写入失败:', code, body)
sys.exit(1)
# 校验:读回单元格确认写入成功
check_vals = read_range(token, range_cell)
got = ''
if check_vals and len(check_vals) > 0:
row0 = check_vals[0]
got = (row0[0] if isinstance(row0, (list, tuple)) and len(row0) > 0 else (row0 if not isinstance(row0, (list, tuple)) else '')) or ''
got = str(got)
expect_head = minutes_text[:40].replace('\n', ' ')
if got and (expect_head[:25] in got or minutes_text[:25] in got):
print(f'✅ 已写入派对智能纪要到「今日总结」→ 2月{date_col_str}日列,校验通过')
elif got:
print(f'✅ 已写入派对智能纪要到「今日总结」→ 2月{date_col_str}日列(校验:已读回 {len(got)} 字)')
else:
print(f'✅ 已写入派对智能纪要到「今日总结」→ 2月{date_col_str}日列(校验未读回,可能需稍后刷新表格查看)')
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,21 @@
# 10 秒视频 · 程序化模板
> 来源:木叶 Remotion程序化视频 | 卡若AI 品牌短片
## 说明
竖屏 1080×192010 秒30fps。渐变背景 + 标题/副标题动效 + 装饰元素。
## 使用
```bash
# 预览
npm run dev
# 渲染到指定路径
npx remotion render src/index.ts Video10s /path/to/output.mp4
```
## 输出
默认输出到:`/Users/karuo/Documents/卡若Ai的文件夹/导出/程序化视频/`

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
{
"name": "10s-video",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "remotion studio src/index.ts",
"render": "remotion render src/index.ts Video10s"
},
"dependencies": {
"remotion": "^4.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remotion/cli": "^4.0.0",
"@types/react": "^18.2.0",
"typescript": "^5.0.0"
}
}

View File

@@ -0,0 +1,20 @@
import React from 'react';
import { Composition } from 'remotion';
import { Video10s } from './Video10s';
const FPS = 30;
const DURATION_SEC = 10;
export const RemotionRoot: React.FC = () => (
<>
<Composition
id="Video10s"
component={Video10s}
durationInFrames={FPS * DURATION_SEC}
fps={FPS}
width={1080}
height={1920}
defaultProps={{}}
/>
</>
);

View File

@@ -0,0 +1,120 @@
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
export const Video10s: React.FC = () => {
const frame = useCurrentFrame();
const fps = 30;
// 0-1.5s: 标题淡入
const titleOpacity = interpolate(
frame,
[0, 30, 45],
[0, 1, 1],
{ easing: Easing.out(Easing.cubic) }
);
const titleScale = interpolate(
frame,
[0, 45],
[0.8, 1],
{ easing: Easing.out(Easing.back({ overshoot: 1.2 })) }
);
// 2-4s: 副标题滑入
const subOpacity = interpolate(
frame,
[60, 90],
[0, 1],
{ easing: Easing.out(Easing.cubic) }
);
const subY = interpolate(
frame,
[60, 90],
[40, 0],
{ easing: Easing.out(Easing.cubic) }
);
// 6-8s: 装饰元素
const accentOpacity = interpolate(
frame,
[180, 210],
[0, 0.6],
{ easing: Easing.out(Easing.cubic) }
);
const accentScale = interpolate(
frame,
[180, 240],
[0.5, 1],
{ easing: Easing.out(Easing.elastic(1)) }
);
return (
<div
style={{
width: '100%',
height: '100%',
background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a3e 50%, #0d1b2a 100%)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
fontFamily: 'system-ui, -apple-system, sans-serif',
}}
>
{/* 主标题 */}
<div
style={{
opacity: titleOpacity,
transform: `scale(${titleScale})`,
fontSize: 72,
fontWeight: 700,
color: '#fff',
letterSpacing: '0.1em',
marginBottom: 24,
textShadow: '0 0 40px rgba(100, 200, 255, 0.4)',
}}
>
AI
</div>
{/* 副标题 */}
<div
style={{
opacity: subOpacity,
transform: `translateY(${subY}px)`,
fontSize: 28,
color: 'rgba(255,255,255,0.85)',
letterSpacing: '0.2em',
}}
>
</div>
{/* 6-8s 装饰圆 */}
<div
style={{
position: 'absolute',
bottom: 160,
width: 120,
height: 120,
borderRadius: '50%',
border: '3px solid rgba(100, 200, 255, 0.5)',
opacity: accentOpacity,
transform: `scale(${accentScale})`,
}}
/>
{/* 底部品牌条 8-10s */}
<div
style={{
position: 'absolute',
bottom: 80,
opacity: interpolate(frame, [240, 270], [0, 1], { easing: Easing.out(Easing.cubic) }),
fontSize: 18,
color: 'rgba(255,255,255,0.6)',
}}
>
· ·
</div>
</div>
);
};

View File

@@ -0,0 +1,4 @@
import { registerRoot } from 'remotion';
import { RemotionRoot } from './Root';
registerRoot(RemotionRoot);

View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}

View File

@@ -54,3 +54,4 @@
| 2026-02-22 05:58:01 | 🔄 卡若AI 同步 2026-02-22 05:57 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 5 个 |
| 2026-02-22 05:58:39 | 🔄 卡若AI 同步 2026-02-22 05:58 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 |
| 2026-02-22 05:59:00 | 🔄 卡若AI 同步 2026-02-22 05:58 | 更新:金仓、水溪整理归档、运营中枢工作台 | 排除 >20MB: 5 个 |
| 2026-02-22 06:08:29 | 🔄 卡若AI 同步 2026-02-22 06:08 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 |

View File

@@ -57,3 +57,4 @@
| 2026-02-22 05:58:01 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 05:57 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-22 05:58:39 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 05:58 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-22 05:59:00 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 05:58 | 更新:金仓、水溪整理归档、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-22 06:08:29 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-22 06:08 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |