yaohao 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/bin/yaohao.js +47 -0
  4. package/package.json +53 -0
  5. package/skills/yaohao/SKILL.md +128 -0
  6. package/src/commands/calendar.js +29 -0
  7. package/src/commands/cron.js +87 -0
  8. package/src/commands/eligibility.js +28 -0
  9. package/src/commands/family.js +17 -0
  10. package/src/commands/history.js +17 -0
  11. package/src/commands/init.js +102 -0
  12. package/src/commands/market.js +40 -0
  13. package/src/commands/notify.js +92 -0
  14. package/src/commands/result.js +18 -0
  15. package/src/commands/set.js +53 -0
  16. package/src/commands/status.js +17 -0
  17. package/src/commands/waitlist.js +17 -0
  18. package/src/commands/watch.js +159 -0
  19. package/src/constants.js +18 -0
  20. package/src/lib/config-manager.js +67 -0
  21. package/src/lib/notifier.js +190 -0
  22. package/src/output.js +15 -0
  23. package/src/source/_shared/crawl.js +169 -0
  24. package/src/source/_shared/parseUtils.js +141 -0
  25. package/src/source/_shared/titleClassify.js +30 -0
  26. package/src/source/beijing/calendar.js +65 -0
  27. package/src/source/beijing/constants.js +8 -0
  28. package/src/source/beijing/crawl.js +156 -0
  29. package/src/source/beijing/eligibility.js +110 -0
  30. package/src/source/beijing/index.js +23 -0
  31. package/src/source/beijing/parse.js +206 -0
  32. package/src/source/beijing/pdfExtract.js +41 -0
  33. package/src/source/beijing/service.js +190 -0
  34. package/src/source/guangzhou/calendar.js +54 -0
  35. package/src/source/guangzhou/constants.js +16 -0
  36. package/src/source/guangzhou/eligibility.js +88 -0
  37. package/src/source/guangzhou/index.js +22 -0
  38. package/src/source/guangzhou/parse.js +61 -0
  39. package/src/source/guangzhou/service.js +126 -0
  40. package/src/source/hangzhou/calendar.js +60 -0
  41. package/src/source/hangzhou/constants.js +16 -0
  42. package/src/source/hangzhou/eligibility.js +102 -0
  43. package/src/source/hangzhou/index.js +20 -0
  44. package/src/source/hangzhou/parse.js +59 -0
  45. package/src/source/hangzhou/service.js +122 -0
  46. package/src/source/index.js +54 -0
  47. package/src/source/shenzhen/calendar.js +44 -0
  48. package/src/source/shenzhen/constants.js +14 -0
  49. package/src/source/shenzhen/eligibility.js +90 -0
  50. package/src/source/shenzhen/index.js +20 -0
  51. package/src/source/shenzhen/parse.js +58 -0
  52. package/src/source/shenzhen/service.js +122 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chaos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # yaohao
2
+
3
+ [![npm version](https://img.shields.io/npm/v/yaohao.svg)](https://www.npmjs.com/package/yaohao)
4
+ [![npm downloads](https://img.shields.io/npm/dm/yaohao.svg)](https://www.npmjs.com/package/yaohao)
5
+ [![CI](https://github.com/GuangyuZhan/yaohao/actions/workflows/ci.yml/badge.svg)](https://github.com/GuangyuZhan/yaohao/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![Node](https://img.shields.io/node/v/yaohao.svg)](https://nodejs.org/)
8
+
9
+ 国内城市车牌摇号 CLI 工具 —— 让人类和 AI Agent 都能在终端做资格自检、查询摇号形势、订阅开奖结果与政策变化。
10
+
11
+ 支持城市:**北京** · **广州** · **深圳** · **杭州**
12
+
13
+ > ⚠️ **不保证持续可用**:本工具依赖各城官方公告页结构,官网改版可能导致部分功能失效。Issue 跟踪修复,无 SLA 承诺。
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ npm install -g yaohao
19
+ ```
20
+
21
+ 要求 Node.js >= 20。
22
+
23
+ ## 快速开始
24
+
25
+ ```bash
26
+ # 1. 初始化(交互式选默认城市 / 指标类型 / 申请人类型)
27
+ yaohao init
28
+
29
+ # 2. 看本年关键日历(窗口期、摇号日)
30
+ yaohao calendar # 默认城市
31
+ yaohao calendar --city shenzhen # 指定城市
32
+
33
+ # 3. 自己能不能摇号
34
+ yaohao eligibility --city beijing
35
+
36
+ # 4. 当期形势
37
+ yaohao market --city guangzhou
38
+
39
+ # 5. 订阅开奖 + 政策推送
40
+ yaohao notify add bark://your-bark-key
41
+ yaohao watch result --city beijing # 首次记录历史,下次起检测新增
42
+ yaohao cron setup # 写 crontab,默认每天 9:00 检查
43
+ ```
44
+
45
+ ## 城市能力矩阵
46
+
47
+ | 命令 | 北京 | 广州 | 深圳 | 杭州 |
48
+ |---|---|---|---|---|
49
+ | `calendar` 关键日历 | ✅ | ✅ | ✅ | ✅ |
50
+ | `eligibility` 资格自检 | ✅ | ✅ | ✅ | ✅ |
51
+ | `market` 形势播报 | ✅ 含 PDF 中签率 | ✅ HTML 字段 | ✅ HTML 字段 | ✅ HTML 字段 |
52
+ | `watch result` 开奖订阅 | ✅ | ✅ | ✅ | ✅ |
53
+ | `watch policy` 政策订阅 | ✅ | ✅ | ✅ | ✅ |
54
+ | `watch window` 窗口期订阅 | ✅ | ✅ | ✅ | ✅ |
55
+
56
+ 各城节奏差异:
57
+
58
+ | 城市 | 申请窗口 | 摇号日 |
59
+ |---|---|---|
60
+ | 北京 | 每年 1/1-3/8、8/1-10/8 | 4 月、12 月 |
61
+ | 广州 | 每月 12 日截止 | 每月 25 日 |
62
+ | 深圳 | 每月 8 日截止(9 日入次月) | 每月 26 日 |
63
+ | 杭州 | 每月 1-8 日申报 | 每月 26 日 + 阶梯摇号年度专场 |
64
+
65
+ ## 命令参考
66
+
67
+ ### 全局选项
68
+
69
+ 所有命令支持 `--city <beijing|guangzhou|shenzhen|hangzhou>`,缺省走 `yaohao init` 设置的默认城市。
70
+
71
+ ### `calendar` 关键日历
72
+
73
+ ```bash
74
+ yaohao calendar [--city <city>] [--year <year>]
75
+ ```
76
+
77
+ 输出:当前及未来几个月的关键日期(申请截止 / 资格审核 / 摇号日)+ 距离今天的天数。
78
+
79
+ ### `eligibility` 资格自检
80
+
81
+ ```bash
82
+ yaohao eligibility [--city <city>]
83
+ ```
84
+
85
+ 交互式问答:户籍类型 → 年龄 → 驾照 → 名下车牌 → 现有指标 → 社保(非户籍)→ 家庭/阶梯/人才(按城市差异)。
86
+
87
+ 输出:能不能摇号、能摇哪类(普通/新能源/混合动力/家庭等)。
88
+
89
+ ### `market` 形势播报
90
+
91
+ ```bash
92
+ yaohao market [--city <city>] [--no-pdf] [--no-cache] [--json]
93
+ ```
94
+
95
+ 输出:当期申请人数、配置数量、中签率(北京含 PDF 解析的精确数字)、相关公告链接。
96
+
97
+ ### `watch` 订阅
98
+
99
+ ```bash
100
+ yaohao watch result [--city <city>] [--no-notify] [--no-cache] [--json] # 开奖结果
101
+ yaohao watch policy [--city <city>] # 政策变化
102
+ yaohao watch window [--city <city>] # 申请窗口/资格审核
103
+ ```
104
+
105
+ 首次运行只记录当前历史不推送;后续运行检测新增并通过通知渠道推送。
106
+
107
+ ### `init` / `set` / `notify` / `cron`
108
+
109
+ ```bash
110
+ yaohao init # 交互式初始化
111
+ yaohao init --city beijing --reg-type PTC --apply-type person --notify <url>
112
+
113
+ yaohao set default-city <city> # 改默认城市
114
+ yaohao set reg-type <PTC|XNY> # 改指标类型
115
+ yaohao set apply-type <person|family> # 改申请人类型
116
+
117
+ yaohao notify add <url> # 添加通知渠道
118
+ yaohao notify remove <url>
119
+ yaohao notify test
120
+
121
+ yaohao cron setup [--schedule '0 9 * * *'] # 一键写 crontab
122
+ yaohao cron status
123
+ yaohao cron remove
124
+ ```
125
+
126
+ 通知 URL 兼容 [Apprise](https://github.com/caronc/apprise) 格式,支持 Bark / Telegram / 钉钉 / 企微 / 飞书 / Slack / Webhook。
127
+
128
+ ## v1 不支持
129
+
130
+ 以下命令在 v1 不实现(出于隐私和合规考虑,工具不持有用户密码):
131
+
132
+ | 命令 | 提示 |
133
+ |---|---|
134
+ | `status` / `family` / `result` / `history` / `waitlist` | 本人账号查询请直接登录各城官网 |
135
+ | `watch renewal` / `watch expiry` / `watch ranking` | 同上 |
136
+
137
+ ## 配置
138
+
139
+ 存储在 `~/.yaohao/config.json`,`yaohao init` 自动创建。缓存在 `~/.yaohao/cache/`。
140
+
141
+ ## AI Agent
142
+
143
+ Skill 文件位于 `skills/yaohao/SKILL.md`,触发关键词包括:摇号、指标、申请编码、家庭摇号、新能源指标、阶梯、中签、北京/广州/深圳/杭州买车等。
144
+
145
+ ```bash
146
+ npx skills add yaohao -g
147
+ ```
148
+
149
+ ## 边界
150
+
151
+ - **仅用公开数据**:所有数据来自各城官方公告页(公开 HTML + PDF),不调用任何需要登录的接口
152
+ - **不持有用户密码**:所有配置仅存本地,请求直连官方域名,不经任何第三方中转
153
+ - **不做任何写入操作**:不替用户提交申请、不修改身份信息、不代他人查询
154
+ - **MIT 开源 + 无变现**
155
+
156
+ ## 致谢
157
+
158
+ - [`fichas/cross_beijing_cli`](https://github.com/fichas/cross_beijing_cli) — 进京证 CLI,本项目脚手架来源
159
+ - [`JimmyLiang-lzm/BJJTW_Get`](https://github.com/JimmyLiang-lzm/BJJTW_Get) — 北京交通委接口参考
160
+
161
+ ## License
162
+
163
+ MIT
package/bin/yaohao.js ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createRequire } from 'module';
4
+ const require = createRequire(import.meta.url);
5
+ const { version } = require('../package.json');
6
+
7
+ import { program } from 'commander';
8
+
9
+ program
10
+ .name('yaohao')
11
+ .description('国内城市车牌摇号 CLI - 资格自检、形势播报、开奖订阅、政策提醒(北京 / 广州 / 深圳 / 杭州)')
12
+ .version(version);
13
+
14
+ import { registerInitCommand } from '../src/commands/init.js';
15
+ import { registerEligibilityCommand } from '../src/commands/eligibility.js';
16
+ import { registerCalendarCommand } from '../src/commands/calendar.js';
17
+ import { registerMarketCommand } from '../src/commands/market.js';
18
+ import { registerWatchCommand } from '../src/commands/watch.js';
19
+ import { registerStatusCommand } from '../src/commands/status.js';
20
+ import { registerFamilyCommand } from '../src/commands/family.js';
21
+ import { registerResultCommand } from '../src/commands/result.js';
22
+ import { registerHistoryCommand } from '../src/commands/history.js';
23
+ import { registerWaitlistCommand } from '../src/commands/waitlist.js';
24
+ import { registerNotifyCommand } from '../src/commands/notify.js';
25
+ import { registerSetCommand } from '../src/commands/set.js';
26
+ import { registerCronCommand } from '../src/commands/cron.js';
27
+
28
+ // 无登录态(任何人能用)
29
+ registerEligibilityCommand(program);
30
+ registerCalendarCommand(program);
31
+ registerMarketCommand(program);
32
+ registerWatchCommand(program);
33
+
34
+ // 登录态查询
35
+ registerInitCommand(program);
36
+ registerStatusCommand(program);
37
+ registerFamilyCommand(program);
38
+ registerResultCommand(program);
39
+ registerHistoryCommand(program);
40
+ registerWaitlistCommand(program);
41
+
42
+ // 脚手架
43
+ registerNotifyCommand(program);
44
+ registerSetCommand(program);
45
+ registerCronCommand(program);
46
+
47
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "yaohao",
3
+ "version": "1.0.0",
4
+ "description": "国内城市车牌摇号 CLI(北京 / 广州 / 深圳 / 杭州)- 资格自检、关键日历、形势播报、开奖订阅、政策提醒。AI Agent 友好。",
5
+ "type": "module",
6
+ "bin": {
7
+ "yaohao": "bin/yaohao.js"
8
+ },
9
+ "engines": {
10
+ "node": ">=20"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "src/",
15
+ "skills/",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "keywords": [
20
+ "摇号",
21
+ "小客车",
22
+ "指标",
23
+ "车牌",
24
+ "新能源",
25
+ "家庭摇号",
26
+ "北京",
27
+ "广州",
28
+ "深圳",
29
+ "杭州",
30
+ "beijing",
31
+ "guangzhou",
32
+ "shenzhen",
33
+ "hangzhou",
34
+ "lottery",
35
+ "cli",
36
+ "agent"
37
+ ],
38
+ "license": "MIT",
39
+ "homepage": "https://github.com/GuangyuZhan/yaohao#readme",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/GuangyuZhan/yaohao.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/GuangyuZhan/yaohao/issues"
46
+ },
47
+ "dependencies": {
48
+ "@inquirer/prompts": "^8.3.2",
49
+ "commander": "^14.0.3",
50
+ "pdfjs-dist": "^4.10.38",
51
+ "undici": "^6.0.0"
52
+ }
53
+ }
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: yaohao
3
+ version: 1.0.0
4
+ description: "国内城市车牌摇号 CLI:资格自检、关键日历、形势播报、开奖订阅、政策提醒。覆盖北京、广州、深圳、杭州。当用户提到摇号、指标、申请编码、家庭摇号、新能源指标、阶梯、中签、京牌/粤A/深圳籍/浙A、北京/广州/深圳/杭州买车等关键词时触发。"
5
+ ---
6
+
7
+ # yaohao 国内城市车牌摇号 CLI
8
+
9
+ 帮助用户通过命令行查询和订阅国内 4 城(北京、广州、深圳、杭州)的小客车摇号公开数据。
10
+
11
+ ## 安装检测
12
+
13
+ ```bash
14
+ which yaohao
15
+ ```
16
+
17
+ 如未安装:
18
+
19
+ ```bash
20
+ npm install -g yaohao
21
+ ```
22
+
23
+ ## 支持的城市
24
+
25
+ | 城市 | --city 值 | 节奏 |
26
+ |---|---|---|
27
+ | 北京 | `beijing` | 半年制:1/1-3/8、8/1-10/8 申请,4/12 月摇号 |
28
+ | 广州 | `guangzhou` | 月度:每月 12 日截止申请,25 日摇号 |
29
+ | 深圳 | `shenzhen` | 月度:每月 8 日截止申请,26 日摇号 |
30
+ | 杭州 | `hangzhou` | 月度 + 阶梯:每月 26 日摇号,阶梯摇号每年一次 |
31
+
32
+ ## 核心命令
33
+
34
+ 所有命令支持 `--city <city>` 切换城市,缺省走默认城市。
35
+
36
+ ### 资格自检
37
+
38
+ ```bash
39
+ yaohao eligibility --city <city>
40
+ ```
41
+
42
+ 交互式问答:户籍 / 年龄 / 驾照 / 名下车牌 / 社保(非户籍)等,输出能不能摇号、能摇哪类。
43
+
44
+ ### 关键日历
45
+
46
+ ```bash
47
+ yaohao calendar --city <city>
48
+ ```
49
+
50
+ 输出当前及未来几个月的申请截止 / 摇号日,附"距今天还有 N 天"。
51
+
52
+ ### 形势播报
53
+
54
+ ```bash
55
+ yaohao market --city <city>
56
+ ```
57
+
58
+ 输出当期申请人数、配置指标、中签率(北京含 PDF 解析的精确数字)、最新公告链接。
59
+
60
+ ### 订阅推送
61
+
62
+ ```bash
63
+ yaohao watch result --city <city> # 开奖结果
64
+ yaohao watch policy --city <city> # 政策变化
65
+ yaohao watch window --city <city> # 申请窗口 / 资格审核
66
+ ```
67
+
68
+ 首次运行只记录历史,下次起检测新增并通过通知渠道推送。
69
+
70
+ ## 初始化检测
71
+
72
+ ```bash
73
+ yaohao init # 没初始化时提示用户跑这个
74
+ ```
75
+
76
+ 也支持非交互式:
77
+
78
+ ```bash
79
+ yaohao init --city beijing --reg-type PTC --apply-type person
80
+ ```
81
+
82
+ ## 通知渠道
83
+
84
+ ```bash
85
+ yaohao notify add <url> # URL 格式兼容 Apprise: bark://, tgram://, dingtalk://, wecom://, feishu://, slack://
86
+ yaohao notify test
87
+ ```
88
+
89
+ ## 定时
90
+
91
+ ```bash
92
+ yaohao cron setup # 默认每天 9:00 检查公告
93
+ yaohao cron status
94
+ yaohao cron remove
95
+ ```
96
+
97
+ ## 用户场景示例
98
+
99
+ ### 场景 A:刚来北京 / 广州 / 深圳 / 杭州,想知道能不能摇号
100
+
101
+ ```bash
102
+ yaohao eligibility --city beijing
103
+ yaohao calendar --city beijing
104
+ ```
105
+
106
+ ### 场景 B:已经在摇,想被开奖结果通知
107
+
108
+ ```bash
109
+ yaohao notify add bark://xxx
110
+ yaohao watch result --city shenzhen
111
+ yaohao cron setup
112
+ ```
113
+
114
+ ### 场景 C:政策可能要变,想被通知
115
+
116
+ ```bash
117
+ yaohao watch policy --city guangzhou
118
+ ```
119
+
120
+ ## 边界
121
+
122
+ - v1 仅做公开数据查询和推送,**不做任何登录态查询**(不持有用户密码,不查本人状态/排名)
123
+ - 本人申请状态请直接登录各城官网:
124
+ - 北京:https://apply.jtw.beijing.gov.cn/apply/
125
+ - 广州:https://jtzl.jtj.gz.gov.cn/
126
+ - 深圳:https://xqctk.jtys.sz.gov.cn/
127
+ - 杭州:https://hzxkctk.cn/(或浙里办 APP)
128
+ - 不保证持续可用 —— 依赖官方公告页结构,改版可能导致部分功能失效
@@ -0,0 +1,29 @@
1
+ import { getSource, listImplemented, getCityLabel } from '../source/index.js';
2
+ import { getUser } from '../lib/config-manager.js';
3
+ import { DEFAULT_CITY } from '../constants.js';
4
+ import { output, success, error } from '../output.js';
5
+
6
+ function resolveCity(opts) {
7
+ if (opts.city) return opts.city;
8
+ const user = getUser();
9
+ return user?.default_city || DEFAULT_CITY;
10
+ }
11
+
12
+ export function registerCalendarCommand(program) {
13
+ program
14
+ .command('calendar')
15
+ .description('关键日历:申请窗口、摇号日、资格审核结果公布日')
16
+ .option('--city <city>', `城市 (${listImplemented().join('|')})`)
17
+ .option('--year <year>', '查询年份', String(new Date().getFullYear()))
18
+ .action(async (opts) => {
19
+ try {
20
+ const city = resolveCity(opts);
21
+ const source = getSource(city);
22
+ const result = source.getCalendar(Number(opts.year));
23
+ output(success({ city, ...result }, result.lines.join('\n')));
24
+ } catch (err) {
25
+ output(error(err.message));
26
+ process.exitCode = 1;
27
+ }
28
+ });
29
+ }
@@ -0,0 +1,87 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { output, success, error } from '../output.js';
3
+
4
+ const MARKER = '# yaohao auto watch';
5
+ const SCHEDULE = '0 9 * * *';
6
+
7
+ function getCurrentCrontab() {
8
+ try {
9
+ return execSync('crontab -l', { encoding: 'utf-8' });
10
+ } catch {
11
+ return '';
12
+ }
13
+ }
14
+
15
+ function getBinPath() {
16
+ try {
17
+ return execSync('which yaohao', { encoding: 'utf-8' }).trim();
18
+ } catch {
19
+ return 'yaohao';
20
+ }
21
+ }
22
+
23
+ export function registerCronCommand(program) {
24
+ const cron = program.command('cron').description('定时任务管理');
25
+
26
+ cron
27
+ .command('setup')
28
+ .description('设置每日定时检查公告 + 推送(默认每天 9:00)')
29
+ .option('--schedule <cron>', 'cron 表达式(如 "0 9 * * *")', SCHEDULE)
30
+ .action(async (options) => {
31
+ try {
32
+ let current = getCurrentCrontab();
33
+ if (current.includes(MARKER)) {
34
+ current = current.split('\n').filter((l) => !l.includes(MARKER)).join('\n');
35
+ }
36
+ const binPath = getBinPath();
37
+ const schedule = options.schedule;
38
+ const cronLine = `${schedule} ${binPath} watch result --json >> /tmp/yaohao.log 2>&1 ${MARKER}`;
39
+ const newCrontab = current.trimEnd() + (current.trim() ? '\n' : '') + cronLine + '\n';
40
+ const escapedCrontab = newCrontab.replace(/'/g, "'\\''");
41
+ execSync(`printf '%s' '${escapedCrontab}' | crontab -`, { encoding: 'utf-8' });
42
+ output(success({ schedule, command: cronLine }, `定时任务已设置: ${schedule}`));
43
+ } catch (err) {
44
+ output(error(`设置定时任务失败: ${err.message}`));
45
+ process.exitCode = 1;
46
+ }
47
+ });
48
+
49
+ cron
50
+ .command('remove')
51
+ .description('移除定时任务')
52
+ .action(async () => {
53
+ try {
54
+ const current = getCurrentCrontab();
55
+ if (!current.includes(MARKER)) {
56
+ output(success(null, '未找到 yaohao 定时任务'));
57
+ return;
58
+ }
59
+ const filtered = current.split('\n').filter((l) => !l.includes(MARKER)).join('\n');
60
+ const escapedCrontab = filtered.replace(/'/g, "'\\''");
61
+ execSync(`printf '%s' '${escapedCrontab}' | crontab -`, { encoding: 'utf-8' });
62
+ output(success(null, '定时任务已移除'));
63
+ } catch (err) {
64
+ output(error(`移除定时任务失败: ${err.message}`));
65
+ process.exitCode = 1;
66
+ }
67
+ });
68
+
69
+ cron
70
+ .command('status')
71
+ .description('查看定时任务状态')
72
+ .action(async () => {
73
+ try {
74
+ const current = getCurrentCrontab();
75
+ const exists = current.includes(MARKER);
76
+ if (exists) {
77
+ const line = current.split('\n').find((l) => l.includes(MARKER));
78
+ output(success({ active: true, line }, `定时任务已启用: ${line}`));
79
+ } else {
80
+ output(success({ active: false }, '定时任务未设置'));
81
+ }
82
+ } catch (err) {
83
+ output(error(`查询定时任务状态失败: ${err.message}`));
84
+ process.exitCode = 1;
85
+ }
86
+ });
87
+ }
@@ -0,0 +1,28 @@
1
+ import { getSource, listImplemented } from '../source/index.js';
2
+ import { getUser } from '../lib/config-manager.js';
3
+ import { DEFAULT_CITY } from '../constants.js';
4
+ import { output, success, error } from '../output.js';
5
+
6
+ function resolveCity(opts) {
7
+ if (opts.city) return opts.city;
8
+ const user = getUser();
9
+ return user?.default_city || DEFAULT_CITY;
10
+ }
11
+
12
+ export function registerEligibilityCommand(program) {
13
+ program
14
+ .command('eligibility')
15
+ .description('资格自检:回答几个问题判断是否满足摇号申请条件')
16
+ .option('--city <city>', `城市 (${listImplemented().join('|')})`)
17
+ .action(async (opts) => {
18
+ try {
19
+ const city = resolveCity(opts);
20
+ const source = getSource(city);
21
+ const result = await source.checkEligibility();
22
+ output(success({ city, ...result }, result.lines.join('\n')));
23
+ } catch (err) {
24
+ output(error(err.message));
25
+ process.exitCode = 1;
26
+ }
27
+ });
28
+ }
@@ -0,0 +1,17 @@
1
+ import { output, success } from '../output.js';
2
+
3
+ // v1 范围:仅做公开数据查询和提醒。家庭积分/排名查询请登录官网。
4
+ export function registerFamilyCommand(program) {
5
+ program
6
+ .command('family')
7
+ .description('(v1 不支持)家庭摇号积分、阶梯、当前排名')
8
+ .action(async () => {
9
+ output(success(
10
+ { supported: false },
11
+ [
12
+ '家庭摇号积分和排名查询 v1 不支持(基于隐私和合规考虑)。',
13
+ '请直接登录官网查看:https://apply.jtw.beijing.gov.cn/apply/',
14
+ ].join('\n'),
15
+ ));
16
+ });
17
+ }
@@ -0,0 +1,17 @@
1
+ import { output, success } from '../output.js';
2
+
3
+ // v1 范围:本人历史参与记录查询请登录官网。
4
+ export function registerHistoryCommand(program) {
5
+ program
6
+ .command('history')
7
+ .description('(v1 不支持)历史参与记录(参与了多少期、什么时候开始)')
8
+ .action(async () => {
9
+ output(success(
10
+ { supported: false },
11
+ [
12
+ '本人历史参与记录 v1 不支持(基于隐私和合规考虑)。',
13
+ '请直接登录官网查看:https://apply.jtw.beijing.gov.cn/apply/',
14
+ ].join('\n'),
15
+ ));
16
+ });
17
+ }