Repository navigation
Expand file tree
/
Copy pathposition.js
More file actions
99 lines (93 loc) · 3.45 KB
/
Copy pathposition.js
File metadata and controls
99 lines (93 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
* shipxy position — 只查船舶实时位置(登录态)
*
* 输入:MMSI 列表;也支持 --kw
* 输出:mmsi/name/lat/lon/sog_kn/cog_deg/hdg_deg/dest/lastPositionUtc
* —— 是 info 的位置子集,方便脚本化。
*
* 策略:INTERCEPT + browser,同 info,共用 GetShipm 端点。
*
* 例:
* opencli shipxy position --mmsi 413543265
* opencli shipxy position --mmsi "413543265,574952645" --format csv
*/
import { cli, Strategy } from '@jackwener/opencli/registry';
import { ArgumentError, EmptyResultError } from '@jackwener/opencli/errors';
import {
ensureShipxyReady,
fetchShipmByMmsi,
normalizeShipm,
searchByKeyword,
} from './shared/helpers.js';
cli({
site: 'shipxy',
name: 'position',
access: 'read',
description: '船讯网船舶实时位置:经纬度 + 航速 + 航向 + 目的地 + 更新时间',
domain: 'www.shipxy.com',
strategy: Strategy.INTERCEPT,
browser: true,
example: 'opencli shipxy position --mmsi 413543265',
args: [
{ name: 'mmsi', type: 'string', default: '', help: 'MMSI 列表(逗号/空格分隔),与 --kw 二选一' },
{ name: 'kw', type: 'string', default: '', help: '关键字(先搜索再取每个 kw 的第一条),与 --mmsi 二选一' },
],
columns: ['mmsi', 'name', 'lat', 'lon', 'sog_kn', 'cog_deg', 'hdg_deg', 'dest', 'eta', 'lastPositionUtc'],
func: async (page, args) => {
const mmsiRaw = String(args.mmsi ?? '').trim();
const kwRaw = String(args.kw ?? '').trim();
if (!mmsiRaw && !kwRaw) {
throw new ArgumentError('必须提供 --mmsi 或 --kw');
}
let mmsiList = [];
if (mmsiRaw) {
mmsiList = mmsiRaw.split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
for (const m of mmsiList) {
if (!/^\d{7,9}$/.test(m)) {
throw new ArgumentError(`MMSI 格式非法:${m}(应为 7~9 位数字)`);
}
}
} else {
const kws = kwRaw.split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
for (const kw of kws) {
const ships = await searchByKeyword(kw, { limit: 1 });
if (ships.length && ships[0].m) mmsiList.push(String(ships[0].m));
}
if (mmsiList.length === 0) {
throw new EmptyResultError('shipxy position', `--kw 未搜到任何船:${kwRaw}`);
}
}
mmsiList = Array.from(new Set(mmsiList));
await ensureShipxyReady(page);
const rows = [];
for (const mmsi of mmsiList) {
try {
const raw = await fetchShipmByMmsi(page, mmsi);
const norm = normalizeShipm(raw);
if (!norm) {
rows.push({ mmsi, name: '[no-data]', lat: null, lon: null, sog_kn: null, cog_deg: null, hdg_deg: null, dest: '', eta: '', lastPositionUtc: '' });
} else {
rows.push({
mmsi: norm.mmsi,
name: norm.name,
lat: norm.lat,
lon: norm.lon,
sog_kn: norm.sog_kn,
cog_deg: norm.cog_deg,
hdg_deg: norm.hdg_deg,
dest: norm.dest,
eta: norm.eta,
lastPositionUtc: norm.lastPositionUtc,
});
}
} catch (e) {
if (e && e.name === 'AuthRequiredError') throw e;
rows.push({ mmsi, name: `[error] ${e.message || e}`, lat: null, lon: null, sog_kn: null, cog_deg: null, hdg_deg: null, dest: '', eta: '', lastPositionUtc: '' });
}
}
if (rows.length === 0) {
throw new EmptyResultError('shipxy position', '没有查到任何位置');
}
return rows;
},
});