6116 字
约 20 分钟
1
通用型一次性脚本 / CLI 执行流程

通用型一次性脚本 / CLI 执行流程 — 可套用模板

把"一次性(oneshot)命令行程序"从入口到退出的通用流程讲清楚。这份文档只讲流程:做了什么、为什么这么设计,不含任何代码。每一段都用本项目的续火花场景对号入座,帮你把抽象流程落到具体例子上。

适用:定时/手动触发、跑完即退、无常驻服务的脚本(发消息、批量处理、拉报表、单跑爬虫)。不适用常驻 Web 服务。


七段骨架总览

 [入口] → [准备:参数→配置→把关] → [初始化资源] → [核心循环] → [收尾落盘/通知] → [退出码] → 进程退出
    │          │                     │               │            │                    └─ 信号交 cron/systemd/CI
    │          └─ 任何致命错误 → 立即跳收尾,仍保证"落盘+通知+正确退出码"
    └─ 每阶段都可能失败;成败哲学决定继续还是止损(见 §7)

[!note] 怎么读这份文档 每一段都先看 [流程] 这段要做什么(纯讲做什么、为什么),再看 ➤ 续火花这么走(同一段流程套到本项目,也是纯白话)。全程没有代码

通用段 ↔ 本项目流程对照速查:

通用段 流程上对应(续火花) 深入看哪篇笔记
① 入口 程序的启动壳,只派活不干活 [[Main]]
② 参数 先问清"试跑还是真发、用哪份配置" [[Main]]
③ 配置把关 读登录态+任务清单,启动即校验 [[Config-Models]]
④ 抢锁 确认没有别的任务在跑(防重复) [[History]]
⑤ 起资源 建目录、开日志、打开浏览器登进抖音 [[Browser]]
⑥ 核心循环 挨个好友发消息+去重+成败分级 [[Main]]/[[Sender]]
⑦ 收尾/退出码 写结果、尽力通知、给外部结论 [[Notifier]]

① 入口(Entry)—— 极薄,别装业务

[流程] 程序的门口。它不干活,只负责三件事:

  1. 接住你的命令和参数;
  2. 把活派给真正的业务部分;
  3. 把业务部分的结论转成"这趟成没成"的信号(退出码)。

☞ 为什么这么设计?把"接命令"和"干活"分开——想给"干活"部分单独做验证时,不用真的去敲命令行起一次,方便、省事、好测。

➤ 续火花这么走:你发起一次运行 → 进入启动壳 → 启动壳立刻把"发消息"这件正事交给主程序去办。启动壳本人很轻:只负责"接收指令、派活、中途出了情况就换算成'这趟是成功还是失败'"。真正逐条发消息的那些动作,它一概不碰。


② 参数解析(Argument Parsing)—— 先问清楚"这趟怎么跑"

[流程] 程序一起动,做的第一件决策:这趟到底怎么跑?主要确认两件事:

  • 是不是只检查、不真动手(试运行)。这是整个设计里最重要的一道安全阀,尤其对象是那种"会真实改动外部世界"的操作——没试运行就放出去,一个失误就可能真的发出去 / 真的删掉 / 真的下单。
  • 用哪份配置。不同环境(本地、生产)配置可能不同,需要能指定用哪一份。

这两点定下来,才继续往下。

➤ 续火花这么走

  • 试运行:先只验证"能不能登进抖音、能不能定位到你列的那些好友",绝不把消息真正发出去。你改了好友名单、换了问候语、调了表情后,一定要先这样过一遍,看到"全部通过"再放心正式跑——这是防"改错却直接发给真人"的护身符。
  • 指定配置:本地调试用一套配置、正式定时用另一套,靠一个开关切换,不用来回改文件。

③ 配置加载与校验(Config Load + Fail-Fast)—— 提前把关,绝不带病上路

[流程] 动手之前,先把"干活的依据"读进来,并从头到尾审查一遍。依据分两类:

  • 运行设定:怎样跑(放哪、密钥、开关……);
  • 任务内容:具体干些啥(对象清单、每项的细节)。

审查的原则叫 fail-fast(快失败):只要有一处不对,当场停下来、明明白白告诉你"哪一项为什么不行",绝不带着隐患继续

☞ 为什么这么严?因为地方不对,后面注定失败——与其浪费资源、甚至误动外部世界,不如在动手前就拦住。这是"边界守把住、破口子不放行"的思维方式。

➤ 续火花这么走:程序去取登录凭证(之前登录存下的身份),再读任务清单(发给谁、每天发啥、间隔多久、要不要防重、出错要不要继续)。审查就做在这里:

  • 名单一个好友都没写 → 停;
  • 要发的原生表情没配上 → 停;
  • 要发的图片文件不存在 → 停。 开浏览器之前就把这些都拦住,这趟的结论就是"配置有问题"。

④ 进程互斥(Single-Instance Lock)—— 确认没有第二个"我"在跑

[流程] 正式动手前,先抢一道"独占牌":当下有没有另一个同样的任务已经在跑了?

  • 有 → 自己退出,别去凑热闹(否则两个一起会重复处理同一批对象)。
  • 没有 → 牌到手,才放心继续。

☞ 这是防重复的第一道防线。定时任务的重复触发、手动和定时同时发生、双击误启动——全指望它兜住。

➤ 续火花这么走:程序启动时检查"是不是已经有一个发消息的任务在跑":

  • 场景:你设了每天定点自动发消息,同时又手动点了一次——两个险些撞上 → 后启动的那个得知"已有在跑"→ 立刻退出,绝不允许两个进程同时对同一批好友发。
  • 除此之外还有第二道保险:在 CI 平台上让同一个任务不并发。两层一起,把"重复发送"堵死。

⑤ 初始化资源(Resource Setup)—— 备好工具、签好"用完必还"

[流程] 确认没撞车后,程序开始置办干活的家伙

  • 输出目录——结果、出问题的现场都放这里;
  • 日志——一路记下发生了什么;
  • 若是会话型的操作,就打开那个"要打交道的资源"(浏览器/数据库/网络),并且从现在起就把"用完一定归还"的约定锁死:不管中途成没成,结束后都老老实实把资源按序还回去、关干净,绝不留下没关完的后台进程。

☞ 为什么"用完必还"这么郑重?因为资源(尤其浏览器、数据库连接)一旦泄漏,轻则占着不还,重则在后台越攒越多、拖垮机器。用"约定式归还"把它变成默认行为,而不是靠谁记得去关。

➤ 续火花这么走:程序建好输出目录、开好日志,然后打开一个真实浏览器,用登录状态登进抖音——这是整条流程里**唯一"碰得到抖音"**的东西。并且从一开始就用"用完必还"管住它:进去时一步一步启动浏览器、建页面、挂状态;无论这趟成没成,结束时都按序把页面、浏览器、驱动一一关干净,绝不在后台留个空浏览器进程。顺带把"去重记录簿"载入内存(等下判断"今天发过没"要用),并算好"今天"是哪天。


⑥ 核心循环(Main Loop)—— 挨个对象,干正事

[流程] 这是真正干活的地方,核心思路是"拆分 → 逐个跑 → 每个都记结局":

  1. :把一件事拆成一个个独立的小单元。
  2. 逐个跑:每个单元都走"准备 → 动手 → 确认"的小循环。
  3. 记结局:每个单元跑完,记成一个明确的"结果"(成功/失败/跳过/未知),攒成一张清单,供收尾用。

另有两条贯穿的重要规则:

  • 防重(幂等):给每个单元一个稳定编号,并持久化记"处理过没"。已处理过的,下次直接跳过,绝不重复做。
  • 成败分级
    • 大问题(凭据、安全这类全局性坏了)→ 判定所有单元都做不成了 → 立即刹车,后面的都不做;
    • 小问题(单个单元出错)→ 记一笔,默认继续下一个。
  • 节流:对象之间随机歇几秒,让整趟节奏像"人"而不是"机器"。

➤ 续火花这么走一位好友 = 一个单元。对每一位好友走:找到他(搜索→点开聊天)→ 把他今天该发的每条消息发出去 → 确认真的收到

  • 稳定编号:好友名 + 今天的日期 + 那条消息内容算出的代号,并成一个唯一键,用来认"这一条"。
  • 防重:开防重后,发"某位好友的某条消息"前先查——今天发过没? 发过就跳过;没发过,先占个位 → 复查登录 → 真正发 → 发完记"已发"。
    • 关键拿捏:如果发完后没法确认到底发没发出去,程序绝不再发一次——因为它怕"其实发了、只是没看到,再发就重复"。它宁可把这条记成"状态未知",下次当"已处理"跳过。目的:不留任何重复的可能。
  • 节流:两位好友之间随机停几秒,让整趟动作像真人手点,不易被系统判成机器行为。
  • 成败分级:登录失效/风控这类全局问题 → 直接刹车、今天剩下的全不发了;单个好友找不到/发不出 → 记失败,默认继续下一位(可切换成"严格模式:一个失败就全停")。

⑦ 收尾与退出码(Finalize + Exit Code)—— 无论成败都给个交代

[流程] 不管这趟全成功、有失败、还是中途爆了,程序都要收尾,做三件事:

  1. 把结果落成一个文件——谁成功、谁失败、失败原因,机器可读,供排查和自动化用;
  2. 尽力通知——往指定渠道(邮件/IM/webhook)报个信:成了几个、败几个、为啥。通知是尽力而为:就算没发出去,也不掩盖任务本身的结论;
  3. 给外部一个明确结论(退出码)——让定时调度器 / CI 能据此判断要不要告警、要不要重试。

➤ 续火花这么走

  • 结果文件:记下这次是不是试运行、什么时候跑完、每位好友的成败与发出条数。
  • 通知(钉钉):列成功名单、失败名单、失败原因、失败截图、CI 运行链接;通知发不出去,也不影响任务本身成败。
  • 结论(退出码),正好有四种典型结局:
结论 续火花里代表什么
成功 所有好友都发好了
有失败 某些好友失败,但不致命
致命 登录失效 / 风控 / 配置错 / 已锁定
取消 你手动中断了任务

外部就靠这四个结论决定"要不要报警、要不要重跑、要不要人工看看"。


这七段 "为什么这么设计"(一句话版)

阶段的动作 那句"流程大白话"
入口 启动壳只派活,不掺和干活,方便单独验证干活部分
参数 大风险操作必须先有"试运行"这道安全阀
配置把关 不行立刻停,绝不带病上路
抢锁 确认没人抢,免得同一批做两遍
起资源 工具备好、资源打开且约好"用完必还"
核心循环 挨个做、每个记结局;小事记账继续、大事刹车
收尾/退出码 成败都交代结果、尽力通知、给外部明确结论

贯穿全程的三个通用特点

  1. :每步等"该出现的真出现"再动手,不抢跑。
  2. 不重复:抢锁 + 去重记录 + "没确认就不重做",三层防重复。
  3. 有现场:任何一步出问题,都留下"日志 / 记录 / 现场",能快速定位挂在哪。

写你自己脚本时的核对清单

  • 入口只做:接命令 → 派活 → 异常转结论
  • 大风险操作带 试运行 开关
  • 配置先解析成结构、动手前 快失败校验
  • 单实例锁(或说明为何不需要)
  • 运行设定 / 任务内容分开管理
  • 核心循环:逐单元 + 结果分类 + 成败分级
  • 单元有稳定编号,支持防重 / 幂等
  • 打外部资源时,单元间节流 / 随机间隔
  • 收尾无论成败都写结果 + 尽力通知
  • 通知文本做转义 + 长度裁剪
  • 退出码(成功/失败/致命/取消…)约定清晰并文档化
  • 资源用"用完必还"包住,留收尾兜底

续火花业务流程(完整版)

到这里为止是通用骨架;这一整章是把它落到本项目——一次"续火花"任务从头到尾到底怎么走,全程白话、零代码。可以当作整篇文档的"活例子"来读:上面七个阶段的理解,都可以在这条业务流里看到影子。

一张图看懂

 发起
   │
   ▼
 ① 入口:决定这趟怎么跑
 ② 准备配置:读设定 + 读任务清单,提前把关
 ③ 抢锁:确认没有别的任务在跑
 ④ 起资源:建目录、开日志、打开浏览器登进抖音
 ⑤ 进私信主页 & 验明正身(掉线/风控→停)
 ⑥ 逐位好友处理(找到人→发消息→确认;小事记账、大事刹车)
 ⑦ 收尾:写结果文件 + 尽力通知
 ⑧ 退出:关浏览器,给外部一个"成/败/大错/取消"的结论

① 入口 — 决定这趟怎么跑

程序被启动,第一件事是搞清楚"这趟的规矩":

  • 是不是只检查不真发(试运行)?试运行时,只验证"能不能登进去、能不能找到好友",绝不真正发消息——这是改动后做安全验证用的。
  • 用哪份配置?允许指定不同的配置来源(本地/生产换着用)。

一句话:先告诉我试跑还是真发、用哪份配置,我再继续。

→ 对应通用骨架的 ①入口 + ②参数


② 准备配置 — 提前把关

程序取两样东西:

  • 登录凭证:之前登录存下的身份,让自己能进抖音。
  • 任务清单:发给谁、每天发什么、间隔多久、要不要防重、出错是否继续。

关键 = 提前把关。 只要清单里有一处不对——比如要发的表情没配上要发的图片文件不存在——程序当场停,根本不会去打开浏览器。因为它清楚:配置错了后面必失败,早停省时省力、还不白碰账号。

一句话:先检查配置,不行立刻停,绝不带病上路。

→ 对应通用骨架的 ③配置把关


③ 抢锁 — 确认只有我一个在跑

程序要确认:现在是不是已经有一个同样的任务在跑?

  • 有 → 两个并发会重复发送 → 直接退出,不掺和。
  • 没有 → 拿到独占权,才往下。

防重是这条流反复出现的主题:抢锁只是第一道防线,后面发消息阶段还有去重记录这道,双保险都为"不重复"。

一句话:确认没人抢,我才动手,免得同一批消息发两遍。

→ 对应通用骨架的 ④抢锁


④ 起资源 — 建目录、开日志、打开浏览器

程序把"干活的家伙"都备好:

  • 输出目录——结果、出问题的现场都放这;
  • 日志——记这一趟发生了什么;
  • 打开一个真实浏览器,用登录状态登进抖音——这是全程里**唯一"碰得到抖音"**的东西。

这时还没发任何消息,只是"工具就绪、人已到门口"。用"用完必还"管着浏览器:结束了一定按序关干净,不留后台进程。

一句话:目录和日志就绪,浏览器打开,我站到了抖音门口。

→ 对应通用骨架的 ⑤起资源


⑤ 进私信主页 & 验明正身

浏览器打开抖音私信主页后,先做安全检查,回答三个问题:

  1. 页面是不是在催"重新登录"?
  2. 是不是在要"安全验证"(风控)?
  3. 有没有真的进到私信界面,而不是停在别的页?

只要任一异常 → 拍下现场、直接停,今天就不发。 逻辑很硬:账号状态不对时硬来,轻则发不出,重则触发更多风控、伤号。明知状态不对,就不硬上。

一句话:我确认自己还正常登着、没被风控,才敢往下一步走。

→ 对应通用骨架 ⑤ 前半的"验明正身"


⑥ 逐位好友处理 — 核心中的核心

这才是真正"续火花"的地方。对清单里每一位好友,一轮一轮做四件事:

6a 找到这位好友

搜索框输入名字 → 在结果里点开这位朋友的聊天窗口

这是整条流最脆弱的一步——抖音前端经常改版。所以程序准备了好几套不同的找法:这套找不到换一套,总要把它找出来。实在找不到,拍下现场、记为失败、告诉你。

6b 发消息

对该好友今天该发的每条消息(可能一句问候 + 一个表情):

  1. 先复核一次登录状态没掉(防这轮处理太久中途出意外);
  2. 查去重:这条今天是不是已经发过(可能是上次没跑完留下的标记)→ 发过就跳过,不重复;
  3. 真正发出去
  4. 发完确认:回去看聊天记录里这条是不是真出现了。

☞ 这里有个必须讲清楚的"确认拿捏":

  • 确认发出了 → 记下"今天这条已发",安心。
  • 没确认到绝不重发。因为万一其实发出去了、只是没看到,重发不就成了重复?所以宁可"这条算了、不发了",记成"状态未知"。下次再跑,这个"未知"会被当成"已处理过"跳过——为的就是不留任何重复的可能。

6c 记成败

  • 正常发出 → 记"成功",数几条;
  • 这位好友操作出小问题(找不到、发不出)→ 记"失败 + 原因",不耽误下一个
  • 登录失效、风控这种全局性问题 → 所有好友都发不成了 → 整个流程立刻刹车,后面的都不处理。

一句话区分: 小事"记一笔、继续";大事"刹车、全停"。

6d 节奏控制

处理完一个好友,随机停几秒钟再处理下一个——让整趟看起来像一个人在慢慢发,而不是机器狂点,降低被系统盯上的风险。

一句话:挨个找人、把今天的话发出去并确认收到;小事记一笔不误下一个,大事直接刹车;中间还故意歇口气、装得像真人。

→ 对应通用骨架的 ⑥核心循环


⑦ 收尾 — 无论成没成都会做

不管这趟全成功、有失败、还是中途爆了,程序都要收摊:

  • 把结果写成一个文件:谁成功、谁失败、每个失败的原因;
  • 配了通知(如钉钉)就发条消息给你:"成功 x 个,失败 x 个" + 失败原因。

通知是**"尽力而为"**:就算没发出去,也不影响这趟任务本身成没成。更像"事后递个话",递不到就算了。

一句话:跑完或中途停,我都把结果记好、尽力告诉你一声。

→ 对应通用骨架的 ⑦收尾/通知


⑧ 退出 & 交结论

程序关掉浏览器,然后给外部一个明确的信号——这趟到底成没成:

信号 这意味着 外部会怎么反应
成功 全部好友都发好 视为正常,不需处理
有失败 有好友失败,但不致命 可告警,可人工看
大错 登录失效 / 风控 / 配置错 / 撞锁 重点排查,通知负责人
取消 你手动按了中断 看情况决定要不要重跑

这个信号交给定时调度器(定时任务、CI)——它据此判断要不要报警、要不要重试。

一句话:关掉浏览器,给外部一个"成/败/大错/取消"的明确结论。

→ 对应通用骨架的 ⑦退出码


一条线串起续火花全程

发起 → 说清试跑还是真发 → 读配置并提前把关 → 抢锁防重 → 起目录开浏览器 → 进私信页验明正身 → 挨个好友:找到人 → 发消息 → 确认收到,小事记一笔大事刹车,中间随机歇口气 → 写结果 + 尽力通知 → 关浏览器,交给外部一个"成/败/大错/取消"的结论。

续火花的三个特点

  1. :每一步等"该出现的东西真的出现"再动手,不瞎等、不抢跑;
  2. 不重复:抢锁 + 去重记录 + "没确认就不重发"三层,拼命避免发两遍;
  3. 有现场:任何一步出问题,都留下"截图 / 日志 / 记录",让你能快速定位掉在哪。

续火花明细(每个阶段看哪篇笔记)

阶段 深入主题 笔记
入口/编排 主程序如何串起七段 [[Main]]
配置把关 Settings/TaskConfig [[Config-Models]]
抢锁/去重 run.lock + 防重记录 [[History]]
开浏览器/验明正身 登录态与风控检测 [[Browser]]
找到好友 多套找法兜底 [[DouyinChat]]
发消息/确认 发送与"没确认就不重发" [[Sender]]
通知 钉钉 markdown [[Notifier]]

通用骨架见本文档上方各节;续火花完整流程也可直接看 [[Request-Flow]]。使用说明见 README.md

通用型一次性脚本 / CLI 执行流程
http://clxhxhhr.top/posts/551/
作者
clxstart
发布于
2026-09-08
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。