From aeb0176e457dd2a2a4987825dab0bf33b35f4fce Mon Sep 17 00:00:00 2001 From: karuo Date: Mon, 23 Mar 2026 13:58:05 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=84=20=E5=8D=A1=E8=8B=A5AI=20=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=202026-03-23=2013:58=20|=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=9A=E6=B0=B4=E6=A1=A5=E5=B9=B3=E5=8F=B0=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=E3=80=81=E5=8D=A1=E6=9C=A8=E3=80=81=E8=BF=90=E8=90=A5=E4=B8=AD?= =?UTF-8?q?=E6=9E=A2=E5=B7=A5=E4=BD=9C=E5=8F=B0=20|=20=E6=8E=92=E9=99=A4?= =?UTF-8?q?=20>20MB:=2011=20=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../飞书管理/脚本/soul_party_to_feishu_sheet.py | 37 +++++++++++++++++-- .../多平台分发/脚本/distribute_all.py | 37 ++++++++++++++++++- .../多平台分发/脚本/video_metadata.py | 3 ++ .../视频号发布/脚本/channels_api_publish.py | 6 +-- 运营中枢/工作台/gitea_push_log.md | 1 + 运营中枢/工作台/代码管理.md | 1 + 6 files changed, 76 insertions(+), 9 deletions(-) diff --git a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/soul_party_to_feishu_sheet.py b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/soul_party_to_feishu_sheet.py index ca7d4a4a..e231dc18 100644 --- a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/soul_party_to_feishu_sheet.py +++ b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/soul_party_to_feishu_sheet.py @@ -73,8 +73,8 @@ ROWS = { '129': [ 'AI手机金融坏账投流', 200, 0, 250, 14, 187, 4, 561, 21, 31 ], # 130场 2026-03-21:视频号直播结束页 02:25:49≈146min;观众总数2278、最高在线355、新增关注4、总热度3、送礼1;Soul推流无截图数据填0→脚本跳过第5行保留空 '130': [ 'Soul爆量脸视频号问微信', 146, 0, 2278, 0, 3, 1, 3, 4, 355 ], - # 131场 2026-03-23:文章口径场观2580/进房328/128min;视频号结束页补关注4、最高在线75、点赞评论分享计互动;礼物灵魂力无截图填0 - '131': [ '视频号中枢Soul做哨兵', 128, 2580, 328, 0, 2105, 0, 0, 4, 75 ], + # 131场 2026-03-23:视频号结束页 02:05:55≈126min;观众1144、最高75、关注4、点赞1595+评论498+分享12;无总热度/礼物展示填0;Soul推流0→跳过第5行 + '131': [ '视频号中枢Soul哨兵', 126, 0, 1144, 0, 2105, 0, 0, 4, 75 ], } # 场次→按日期列填写时的日期(表头为当月日期 1~31) SESSION_DATE_COLUMN = {'105': '20', '106': '21', '107': '23', '113': '2', '114': '3', '115': '4', '116': '5', '117': '6', '118': '7', '119': '8', '124': '14', '126': '17', '127': '18', '128': '19', '129': '20', '130': '21', '131': '23'} @@ -96,7 +96,6 @@ PARTY_VIDEO_LINKS = { '127': 'https://cunkebao.feishu.cn/minutes/obcnhybw322112tad6916v8r', '129': 'https://cunkebao.feishu.cn/minutes/obcnjb994323l12lhl448177', '130': 'https://cunkebao.feishu.cn/minutes/obcnj1y95z73n53e8m6m1s3j', - # 131场:urls_soul_party.txt 中紧接 130 的新妙记 '131': 'https://cunkebao.feishu.cn/minutes/obcnjzx7dyxco67btud7y8gz', } @@ -112,7 +111,17 @@ TEAM_MEETING_LINKS = { '126': 'https://kcnxrqd5ata7.feishu.cn/minutes/obcng991jg3114b2nj99548d', '127': 'https://cunkebao.feishu.cn/minutes/obcnhxs8usi8c7n27a9f66ux', '129': 'https://kcnxrqd5ata7.feishu.cn/minutes/obcnjbn178iy6919od4119ww', - '131': 'https://kcnxrqd5ata7.feishu.cn/minutes/obcnjzx7dyxco67btud7y8gz', + # 131:与派对同源妙记时可同链;若售商品侧另有团队会议妙记请改此处 + '131': 'https://cunkebao.feishu.cn/minutes/obcnjzx7dyxco67btud7y8gz', +} + +# 补充直链(A 列无独立标签的空行):场次 → [(行号1-based, 完整URL), ...] +# 131:第30行=3月运营报表 Wiki;第32行=今日总结嵌入图 fileToken 对应的云空间文件链(与格内 embed 一致) +SESSION_SUPPLEMENT_ROW_LINKS = { + '131': [ + (30, 'https://cunkebao.feishu.cn/wiki/wikcnIgAGSNHo0t36idHJ668Gfd?sheet=bJR5sA'), + (32, 'https://cunkebao.feishu.cn/file/HNZEb0mxqoW38KxlFLAcKb2Rnef'), + ], } # 小程序当日运营数据:日期号 → {访问次数, 访客, 交易金额},填表时自动写入对应日期列 @@ -225,6 +234,23 @@ def _write_team_meeting_link(token, spreadsheet_token, sheet_id, vals, col_lette print(f'⚠️ 团队会议链接写入未成功: {code} {body}') +def _write_session_supplement_row_links(token, spreadsheet_token, sheet_id, col_letter, session): + """按 SESSION_SUPPLEMENT_ROW_LINKS 写入报表 Wiki、今日总结图文件链等(固定行号)。""" + pairs = (SESSION_SUPPLEMENT_ROW_LINKS or {}).get(session) or [] + for row_num, link in pairs: + link = (link or '').strip() + if not link or not row_num: + continue + rng = f"{sheet_id}!{col_letter}{row_num}:{col_letter}{row_num}" + code, body = update_sheet_range(token, spreadsheet_token, rng, [[link]], value_input_option='USER_ENTERED') + if code == 401 or body.get('code') in (99991677, 99991663): + return + if code == 200 and body.get('code') in (0, None): + print(f'✅ 已写入补充链接 → {col_letter}{row_num}') + else: + print(f'⚠️ 补充链接写入未成功 {col_letter}{row_num}: {code} {body}') + + def load_token(): if not os.path.exists(TOKEN_FILE): print('❌ 未找到飞书 Token 文件:', TOKEN_FILE) @@ -541,6 +567,7 @@ def main(): _write_miniprogram_extra(token, spreadsheet_token, sheet_id, vals, date_col, col_letter, month=month) _write_party_video_link(token, spreadsheet_token, sheet_id, vals, col_letter, session) _write_team_meeting_link(token, spreadsheet_token, sheet_id, vals, col_letter, session) + _write_session_supplement_row_links(token, spreadsheet_token, sheet_id, col_letter, session) _maybe_send_group(session, raw) return print(f'⚠️ 逐格写入成功但校验未通过:{msg}') @@ -563,6 +590,7 @@ def main(): _write_miniprogram_extra(token, spreadsheet_token, sheet_id, vals, date_col, col_letter, month=month) _write_party_video_link(token, spreadsheet_token, sheet_id, vals, col_letter, session) _write_team_meeting_link(token, spreadsheet_token, sheet_id, vals, col_letter, session) + _write_session_supplement_row_links(token, spreadsheet_token, sheet_id, col_letter, session) _maybe_send_group(session, raw) return print(f'⚠️ 写入成功但校验未通过:{msg}') @@ -593,6 +621,7 @@ def main(): _write_miniprogram_extra(token, spreadsheet_token, sheet_id, vals, date_col, col_letter, month=month) _write_party_video_link(token, spreadsheet_token, sheet_id, vals, col_letter, session) _write_team_meeting_link(token, spreadsheet_token, sheet_id, vals, col_letter, session) + _write_session_supplement_row_links(token, spreadsheet_token, sheet_id, col_letter, session) _maybe_send_group(session, raw) return print(f'⚠️ 逐格写入成功但校验未通过:{msg}') diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py index bf55201e..17e29292 100644 --- a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py +++ b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py @@ -29,6 +29,7 @@ import subprocess import sys import time from pathlib import Path +from datetime import datetime, timedelta SCRIPT_DIR = Path(__file__).parent BASE_DIR = SCRIPT_DIR.parent.parent @@ -116,6 +117,32 @@ def print_platform_account_status(all_results: list[PublishResult], targets: lis print(f"{'=' * 60}\n") +def _enforce_channels_schedule_slots( + schedule_times: list | None, + total_videos: int, + *, + min_delay_minutes: int = 10, +) -> list: + """ + 视频号强制定时: + - 若无排期,补一套排期; + - 任一发布时间若过近(<= min_delay_minutes),自动顺延。 + """ + now = datetime.now() + min_dt = now + timedelta(minutes=min_delay_minutes) + if not schedule_times: + schedule_times = [min_dt + timedelta(minutes=55 * i) for i in range(total_videos)] + return schedule_times + + fixed = [] + for i, st in enumerate(schedule_times): + if st <= min_dt: + fixed.append(min_dt + timedelta(minutes=55 * i)) + else: + fixed.append(st) + return fixed + + def _ensure_channels_cookie_or_login(*, auto_login: bool) -> None: """发视频号前对齐双路径 Cookie。默认静默;仅 auto_login 且未设 NO_AUTO_CHANNELS_LOGIN 时才调起扫码。""" sync_channels_cookie_files() @@ -620,8 +647,12 @@ async def _publish_one_round(args: argparse.Namespace) -> tuple[int, int]: if (p, v.name) not in published_set: total_new += 1 + force_channels_timed = "视频号" in targets + effective_now = bool(args.now) and not force_channels_timed + if args.now and force_channels_timed: + print(" [i] 检测到视频号任务:已忽略 --now,改为按时间节点定时发布。") schedule_times = None - if not args.now and total_new > 1: + if (not effective_now and total_new > 1) or force_channels_timed: if args.legacy_schedule: schedule_times = generate_schedule( len(videos), @@ -631,6 +662,8 @@ async def _publish_one_round(args: argparse.Namespace) -> tuple[int, int]: ) else: schedule_times = generate_smart_schedule(len(videos)) + if force_channels_timed: + schedule_times = _enforce_channels_schedule_slots(schedule_times, len(videos)) print(f"\n{'='*60}") print(f" 分发计划 ({mode})") @@ -641,7 +674,7 @@ async def _publish_one_round(args: argparse.Namespace) -> tuple[int, int]: sched_label = "立即发布" if schedule_times: sched_label = "定时排期(智能错峰)" if not args.legacy_schedule else "定时排期(legacy 随机间隔)" - print(f" 发布方式: {sched_label if not args.now else '立即发布'}") + print(f" 发布方式: {sched_label if not effective_now else '立即发布'}") if not args.no_dedup: skipped = len(videos) * len(targets) - total_new if skipped > 0: diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/video_metadata.py b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/video_metadata.py index bf7fa800..b66a86ef 100644 --- a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/video_metadata.py +++ b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/video_metadata.py @@ -152,6 +152,9 @@ class VideoMeta: def hashtags(self, platform: str) -> str: """# 标签字符串""" tags = self._smart_tags(platform) + if platform == "视频号": + # 视频号按新规则:去掉 Soul 相关 # 标签,仅保留业务/品牌相关标签。 + tags = [t for t in tags if "soul" not in t.lower()] parts = [f"#{t}" for t in tags] if platform == "视频号": parts.extend(CHANNELS_FIXED_TAGS) diff --git a/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py b/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py index eeb7035c..1b3804de 100644 --- a/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py +++ b/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py @@ -746,9 +746,9 @@ def _scheduled_ts_for_channels(scheduled_time) -> int: else: ts = int(scheduled_time) now = int(time.time()) - # 2 分钟内视为「立即」,避免 postTime 过近被服务端拒绝 - if ts <= now + 120: - return 0 + # 视频号新规则:统一走定时,不走立即发表;过近时间点自动顺延到 10 分钟后。 + if ts <= now + 600: + return now + 600 return ts diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 9deb31d7..36a08022 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -423,3 +423,4 @@ | 2026-03-22 21:22:06 | 🔄 卡若AI 同步 2026-03-22 21:22 | 更新:Cursor规则、金仓、卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-23 09:48:42 | [强制] 🔄 卡若AI 同步 2026-03-23 09:48 | 更新:Cursor规则、金仓、水桥平台对接、卡木、火炬、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-23 13:36:13 | [强制] 🔄 卡若AI 同步 2026-03-23 13:35 | 更新:水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | +| 2026-03-23 13:49:45 | [强制] 🔄 卡若AI 同步 2026-03-23 13:49 | 更新:金仓、水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index 52fb5af9..66c1d03e 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -426,3 +426,4 @@ | 2026-03-22 21:22:06 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-22 21:22 | 更新:Cursor规则、金仓、卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-23 09:48:42 | 成功(强制) | 成功 | 🔄 卡若AI 同步 2026-03-23 09:48 | 更新:Cursor规则、金仓、水桥平台对接、卡木、火炬、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-23 13:36:13 | 成功(强制) | 成功 | 🔄 卡若AI 同步 2026-03-23 13:35 | 更新:水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-03-23 13:49:45 | 成功(强制) | 成功 | 🔄 卡若AI 同步 2026-03-23 13:49 | 更新:金仓、水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |