job-pro 0.7.2 → 0.7.3
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.
- package/dist/antgroup.js +318 -157
- package/dist/cambricon.js +21 -393
- package/dist/geely.js +29 -57
- package/dist/index.js +1 -1
- package/dist/moka.js +412 -0
- package/package.json +2 -2
package/dist/cambricon.js
CHANGED
|
@@ -3,402 +3,30 @@
|
|
|
3
3
|
// ============================================================
|
|
4
4
|
// API DISCOVERY (probed 2026-05-16)
|
|
5
5
|
//
|
|
6
|
-
// www.cambricon.com
|
|
7
|
-
// in its 加入我们 / careers section. Extracted slugs:
|
|
6
|
+
// www.cambricon.com embeds links to Moka tenant URLs in its 加入我们 section:
|
|
8
7
|
//
|
|
9
8
|
// /campus-recruitment/cambricon/44201 ← campus + intern (main entry)
|
|
10
9
|
// /recommendation-recruitment/cambricon/42452 (referral channel, overlaps)
|
|
11
10
|
// /recommendation-recruitment/cambricon/46261 (referral channel, overlaps)
|
|
12
11
|
//
|
|
13
12
|
// No /social-recruitment/cambricon/<siteId> URL is published — Cambricon
|
|
14
|
-
// only opens 校招 / 实习 publicly through Moka.
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
};
|
|
35
|
-
// ---- helpers (duplicated from megvii.ts — slated for moka.ts refactor) ----
|
|
36
|
-
function htmlDecode(s) {
|
|
37
|
-
return s
|
|
38
|
-
.replace(/"/g, '"')
|
|
39
|
-
.replace(/&/g, "&")
|
|
40
|
-
.replace(/</g, "<")
|
|
41
|
-
.replace(/>/g, ">")
|
|
42
|
-
.replace(/'/g, "'")
|
|
43
|
-
.replace(/'/g, "'");
|
|
44
|
-
}
|
|
45
|
-
function parseInitData(html) {
|
|
46
|
-
const m = html.match(/<input[^>]*id="init-data"[^>]*value="([^"]+)"/);
|
|
47
|
-
if (!m)
|
|
48
|
-
return null;
|
|
49
|
-
try {
|
|
50
|
-
return JSON.parse(htmlDecode(m[1]));
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async function fetchPortalHtml(url) {
|
|
57
|
-
let response;
|
|
58
|
-
try {
|
|
59
|
-
response = await fetch(url, { method: "GET", headers: DEFAULT_HEADERS, redirect: "manual" });
|
|
60
|
-
}
|
|
61
|
-
catch (err) {
|
|
62
|
-
return { ok: false, message: `network error: ${err instanceof Error ? err.message : err}` };
|
|
63
|
-
}
|
|
64
|
-
const cookies = [];
|
|
65
|
-
const headersAny = response.headers;
|
|
66
|
-
if (typeof headersAny.getSetCookie === "function") {
|
|
67
|
-
for (const v of headersAny.getSetCookie.call(response.headers) ?? []) {
|
|
68
|
-
const c = v.split(";")[0];
|
|
69
|
-
if (c)
|
|
70
|
-
cookies.push(c);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (cookies.length === 0) {
|
|
74
|
-
const raw = response.headers.get("set-cookie");
|
|
75
|
-
if (raw)
|
|
76
|
-
cookies.push(...raw.split(/,(?=[^;]+=)/).map((c) => c.split(";")[0].trim()));
|
|
77
|
-
}
|
|
78
|
-
const cookieHeader = cookies.join("; ");
|
|
79
|
-
let r2;
|
|
80
|
-
try {
|
|
81
|
-
r2 = await fetch(url, {
|
|
82
|
-
method: "GET",
|
|
83
|
-
headers: { ...DEFAULT_HEADERS, Cookie: cookieHeader },
|
|
84
|
-
redirect: "follow",
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
catch (err) {
|
|
88
|
-
return { ok: false, message: `network error: ${err instanceof Error ? err.message : err}` };
|
|
89
|
-
}
|
|
90
|
-
if (!r2.ok)
|
|
91
|
-
return { ok: false, status: r2.status, message: `HTTP ${r2.status}` };
|
|
92
|
-
const html = await r2.text();
|
|
93
|
-
return { ok: true, html, cookieHeader, status: r2.status, message: "ok" };
|
|
94
|
-
}
|
|
95
|
-
function decryptMokaEnvelope(envelope, aesIv) {
|
|
96
|
-
if (!envelope.data || !envelope.necromancer)
|
|
97
|
-
return null;
|
|
98
|
-
try {
|
|
99
|
-
const key = Buffer.from(envelope.necromancer, "utf8");
|
|
100
|
-
const iv = Buffer.from(aesIv, "utf8");
|
|
101
|
-
const decipher = createDecipheriv("aes-128-cbc", key, iv);
|
|
102
|
-
const plain = Buffer.concat([
|
|
103
|
-
decipher.update(Buffer.from(envelope.data, "base64")),
|
|
104
|
-
decipher.final(),
|
|
105
|
-
]);
|
|
106
|
-
return JSON.parse(plain.toString("utf8"));
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async function fetchEncryptedPage(pageNum, pageSize, aesIv, cookieHeader) {
|
|
113
|
-
const url = `${API_ENDPOINT}?orgId=${encodeURIComponent(ORG_SLUG)}`;
|
|
114
|
-
const body = {
|
|
115
|
-
orgId: ORG_SLUG,
|
|
116
|
-
siteId: String(CAMPUS_SITE_ID),
|
|
117
|
-
pageNum,
|
|
118
|
-
pageSize,
|
|
119
|
-
needStat: true,
|
|
120
|
-
};
|
|
121
|
-
let response;
|
|
122
|
-
try {
|
|
123
|
-
response = await fetch(url, {
|
|
124
|
-
method: "POST",
|
|
125
|
-
headers: {
|
|
126
|
-
...DEFAULT_HEADERS,
|
|
127
|
-
Accept: "application/json,*/*",
|
|
128
|
-
"Content-Type": "application/json",
|
|
129
|
-
Origin: "https://app.mokahr.com",
|
|
130
|
-
Referer: CAMPUS_URL,
|
|
131
|
-
Cookie: cookieHeader,
|
|
132
|
-
},
|
|
133
|
-
body: JSON.stringify(body),
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
return { ok: false, message: `network error: ${err instanceof Error ? err.message : err}` };
|
|
138
|
-
}
|
|
139
|
-
if (!response.ok)
|
|
140
|
-
return { ok: false, message: `HTTP ${response.status}` };
|
|
141
|
-
let envelope;
|
|
142
|
-
try {
|
|
143
|
-
envelope = await response.json();
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return { ok: false, message: "bad JSON from upstream" };
|
|
147
|
-
}
|
|
148
|
-
const decoded = decryptMokaEnvelope(envelope, aesIv);
|
|
149
|
-
if (!decoded || decoded.code !== 0 || !decoded.data) {
|
|
150
|
-
return { ok: false, message: decoded?.msg || envelope?.msg || "decrypt or upstream error" };
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
ok: true,
|
|
154
|
-
jobs: decoded.data.jobs ?? [],
|
|
155
|
-
total: decoded.data.jobStats?.total ?? 0,
|
|
156
|
-
message: "ok",
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
function buildCityMap(groups) {
|
|
160
|
-
const out = {};
|
|
161
|
-
if (!groups)
|
|
162
|
-
return out;
|
|
163
|
-
for (const g of groups) {
|
|
164
|
-
if (typeof g.cityId === "number" && g.label)
|
|
165
|
-
out[g.cityId] = g.label;
|
|
166
|
-
}
|
|
167
|
-
return out;
|
|
168
|
-
}
|
|
169
|
-
function workCitiesFor(job, cityMap) {
|
|
170
|
-
const cities = (job.locations ?? [])
|
|
171
|
-
.map((l) => {
|
|
172
|
-
if (typeof l.cityId === "number" && cityMap[l.cityId])
|
|
173
|
-
return cityMap[l.cityId];
|
|
174
|
-
return l.country || "";
|
|
175
|
-
})
|
|
176
|
-
.filter((s) => s.length > 0);
|
|
177
|
-
const uniq = [];
|
|
178
|
-
for (const c of cities)
|
|
179
|
-
if (!uniq.includes(c))
|
|
180
|
-
uniq.push(c);
|
|
181
|
-
return uniq.join(" / ");
|
|
182
|
-
}
|
|
183
|
-
function commitmentFor(job) {
|
|
184
|
-
if (typeof job.commitment === "string" && job.commitment.length > 0)
|
|
185
|
-
return job.commitment;
|
|
186
|
-
if (job.hireMode === 1)
|
|
187
|
-
return "全职";
|
|
188
|
-
if (job.hireMode === 2)
|
|
189
|
-
return "实习";
|
|
190
|
-
return "";
|
|
191
|
-
}
|
|
192
|
-
function summarize(job, cityMap) {
|
|
193
|
-
return {
|
|
194
|
-
post_id: String(job.id),
|
|
195
|
-
title: job.title ?? "",
|
|
196
|
-
project: job.zhineng?.name ?? "",
|
|
197
|
-
recruit_label: commitmentFor(job),
|
|
198
|
-
bgs: job.department?.name ?? "",
|
|
199
|
-
work_cities: workCitiesFor(job, cityMap),
|
|
200
|
-
apply_url: `${CAMPUS_URL}#/jobs/${encodeURIComponent(job.id)}`,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
function matchesKeyword(job, kw) {
|
|
204
|
-
if (!kw)
|
|
205
|
-
return true;
|
|
206
|
-
const lc = kw.toLowerCase();
|
|
207
|
-
return ((job.title ?? "").toLowerCase().includes(lc) ||
|
|
208
|
-
(job.zhineng?.name ?? "").toLowerCase().includes(lc) ||
|
|
209
|
-
(job.department?.name ?? "").toLowerCase().includes(lc));
|
|
210
|
-
}
|
|
211
|
-
// ---- searchPositions ----
|
|
212
|
-
export async function searchPositions(opts = {}) {
|
|
213
|
-
const pageSize = opts.pageSize ?? 20;
|
|
214
|
-
const page = opts.page ?? 1;
|
|
215
|
-
const keyword = opts.keyword ?? "";
|
|
216
|
-
const portal = await fetchPortalHtml(CAMPUS_URL);
|
|
217
|
-
if (!portal.ok || !portal.html) {
|
|
218
|
-
return {
|
|
219
|
-
ok: false,
|
|
220
|
-
source: SOURCE,
|
|
221
|
-
message: portal.message,
|
|
222
|
-
query: { keyword, page, pageSize },
|
|
223
|
-
positions: [],
|
|
224
|
-
total: 0,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
const init = parseInitData(portal.html);
|
|
228
|
-
if (!init || !init.jobs || !init.jobStats) {
|
|
229
|
-
return {
|
|
230
|
-
ok: false,
|
|
231
|
-
source: SOURCE,
|
|
232
|
-
message: "Moka init-data missing jobs/jobStats",
|
|
233
|
-
query: { keyword, page, pageSize },
|
|
234
|
-
positions: [],
|
|
235
|
-
total: 0,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
const cityMap = buildCityMap(init.jobsGroupedByLocation);
|
|
239
|
-
let jobs = init.jobs;
|
|
240
|
-
const total = init.jobStats.total ?? jobs.length;
|
|
241
|
-
if (page > 1 && init.aesIv && portal.cookieHeader) {
|
|
242
|
-
const more = await fetchEncryptedPage(page, pageSize, init.aesIv, portal.cookieHeader);
|
|
243
|
-
if (!more.ok || !more.jobs) {
|
|
244
|
-
return {
|
|
245
|
-
ok: false,
|
|
246
|
-
source: SOURCE,
|
|
247
|
-
message: `pagination failed: ${more.message}`,
|
|
248
|
-
query: { keyword, page, pageSize },
|
|
249
|
-
positions: [],
|
|
250
|
-
total,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
jobs = more.jobs;
|
|
254
|
-
}
|
|
255
|
-
const filtered = jobs.filter((j) => matchesKeyword(j, keyword));
|
|
256
|
-
const sliced = filtered.slice(0, pageSize);
|
|
257
|
-
const positions = sliced.map((j) => summarize(j, cityMap));
|
|
258
|
-
return {
|
|
259
|
-
ok: true,
|
|
260
|
-
source: SOURCE,
|
|
261
|
-
query: { keyword, page, pageSize },
|
|
262
|
-
page,
|
|
263
|
-
page_size: pageSize,
|
|
264
|
-
total,
|
|
265
|
-
positions,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
// ---- fetchAllPositions ----
|
|
269
|
-
export async function fetchAllPositions(opts = {}) {
|
|
270
|
-
const pageSize = opts.pageSize ?? 20;
|
|
271
|
-
const maxPages = Math.max(1, opts.maxPages ?? 50);
|
|
272
|
-
const keyword = opts.keyword ?? "";
|
|
273
|
-
const portal = await fetchPortalHtml(CAMPUS_URL);
|
|
274
|
-
if (!portal.ok || !portal.html) {
|
|
275
|
-
return {
|
|
276
|
-
ok: false,
|
|
277
|
-
source: SOURCE,
|
|
278
|
-
message: portal.message,
|
|
279
|
-
total: 0,
|
|
280
|
-
fetched: 0,
|
|
281
|
-
positions: [],
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
const init = parseInitData(portal.html);
|
|
285
|
-
if (!init || !init.jobs || !init.jobStats || !init.aesIv) {
|
|
286
|
-
return {
|
|
287
|
-
ok: false,
|
|
288
|
-
source: SOURCE,
|
|
289
|
-
message: "Moka init-data missing required fields",
|
|
290
|
-
total: 0,
|
|
291
|
-
fetched: 0,
|
|
292
|
-
positions: [],
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
const cityMap = buildCityMap(init.jobsGroupedByLocation);
|
|
296
|
-
const total = init.jobStats.total ?? 0;
|
|
297
|
-
const collected = [...init.jobs];
|
|
298
|
-
let page = 2;
|
|
299
|
-
while (collected.length < total && page <= maxPages) {
|
|
300
|
-
const more = await fetchEncryptedPage(page, pageSize, init.aesIv, portal.cookieHeader ?? "");
|
|
301
|
-
if (!more.ok || !more.jobs || more.jobs.length === 0)
|
|
302
|
-
break;
|
|
303
|
-
collected.push(...more.jobs);
|
|
304
|
-
page += 1;
|
|
305
|
-
}
|
|
306
|
-
const filtered = collected.filter((j) => matchesKeyword(j, keyword));
|
|
307
|
-
return {
|
|
308
|
-
ok: true,
|
|
309
|
-
source: SOURCE,
|
|
310
|
-
total,
|
|
311
|
-
fetched: filtered.length,
|
|
312
|
-
positions: filtered.map((j) => summarize(j, cityMap)),
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
// ---- fetchPositionDetail ----
|
|
316
|
-
export async function fetchPositionDetail(postId) {
|
|
317
|
-
return {
|
|
318
|
-
ok: false,
|
|
319
|
-
source: SOURCE,
|
|
320
|
-
message: "Moka detail endpoint requires the same encrypted-session flow; not implemented. " +
|
|
321
|
-
"Use the apply_url deeplink for the full JD.",
|
|
322
|
-
post_id: postId,
|
|
323
|
-
apply_url: `${CAMPUS_URL}#/jobs/${encodeURIComponent(postId)}`,
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
// ---- fetchDictionaries ----
|
|
327
|
-
export async function fetchDictionaries() {
|
|
328
|
-
const portal = await fetchPortalHtml(CAMPUS_URL);
|
|
329
|
-
if (!portal.ok || !portal.html) {
|
|
330
|
-
return { ok: false, source: SOURCE, message: portal.message };
|
|
331
|
-
}
|
|
332
|
-
const init = parseInitData(portal.html);
|
|
333
|
-
if (!init) {
|
|
334
|
-
return { ok: false, source: SOURCE, message: "Moka init-data missing" };
|
|
335
|
-
}
|
|
336
|
-
return {
|
|
337
|
-
ok: true,
|
|
338
|
-
source: SOURCE,
|
|
339
|
-
locations: init.jobsGroupedByLocation ?? [],
|
|
340
|
-
moka_org: { slug: ORG_SLUG, id: CAMPUS_SITE_ID, url: CAMPUS_URL },
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
// ---- notices (no public endpoint) ----
|
|
344
|
-
const NOTICES_STUB_MSG = "Cambricon (寒武纪): no public notices endpoint on Moka tenant";
|
|
345
|
-
export async function listNotices() {
|
|
346
|
-
return {
|
|
347
|
-
ok: false,
|
|
348
|
-
source: SOURCE,
|
|
349
|
-
message: NOTICES_STUB_MSG,
|
|
350
|
-
notices: [],
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
export async function getNotice(noticeId) {
|
|
354
|
-
return {
|
|
355
|
-
ok: false,
|
|
356
|
-
source: SOURCE,
|
|
357
|
-
message: NOTICES_STUB_MSG,
|
|
358
|
-
notice_id: noticeId,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
export async function findNoticesByQuestion(question, _opts = {}) {
|
|
362
|
-
return {
|
|
363
|
-
ok: false,
|
|
364
|
-
source: SOURCE,
|
|
365
|
-
question,
|
|
366
|
-
message: NOTICES_STUB_MSG,
|
|
367
|
-
matches: [],
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
// ---- matchResume ----
|
|
371
|
-
export async function matchResume(text, opts = {}) {
|
|
372
|
-
const { terms, cities } = extractResumeSignals(text ?? "");
|
|
373
|
-
const candidates = Math.max(20, opts.candidates ?? 100);
|
|
374
|
-
const search = await fetchAllPositions({
|
|
375
|
-
pageSize: 20,
|
|
376
|
-
maxPages: Math.ceil(candidates / 15),
|
|
377
|
-
});
|
|
378
|
-
if (!search.ok) {
|
|
379
|
-
return {
|
|
380
|
-
ok: false,
|
|
381
|
-
source: SOURCE,
|
|
382
|
-
extracted_terms: terms,
|
|
383
|
-
city_preferences: cities,
|
|
384
|
-
matches: [],
|
|
385
|
-
message: search.message,
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
const topN = Math.max(1, opts.topN ?? 10);
|
|
389
|
-
const scored = search.positions
|
|
390
|
-
.map((p) => ({
|
|
391
|
-
p,
|
|
392
|
-
score: scoreOverlap(`${p.title} ${p.project} ${p.bgs}`, terms, cities).score,
|
|
393
|
-
}))
|
|
394
|
-
.sort((a, b) => b.score - a.score)
|
|
395
|
-
.slice(0, topN)
|
|
396
|
-
.map((x) => x.p);
|
|
397
|
-
return {
|
|
398
|
-
ok: true,
|
|
399
|
-
source: SOURCE,
|
|
400
|
-
extracted_terms: terms,
|
|
401
|
-
city_preferences: cities,
|
|
402
|
-
matches: scored,
|
|
403
|
-
};
|
|
404
|
-
}
|
|
13
|
+
// only opens 校招 / 实习 publicly through Moka. Same factory as
|
|
14
|
+
// `cli/src/moka.ts` (used by megvii / geely / etc.).
|
|
15
|
+
import { createAdapter } from "./moka.js";
|
|
16
|
+
const adapter = createAdapter({
|
|
17
|
+
orgSlug: "cambricon",
|
|
18
|
+
label: "Cambricon",
|
|
19
|
+
channels: [
|
|
20
|
+
{ siteId: 44201, kind: "campus-recruitment", recruitType: "campus" },
|
|
21
|
+
],
|
|
22
|
+
defaultRecruitType: "campus",
|
|
23
|
+
});
|
|
24
|
+
export const searchPositions = adapter.searchPositions;
|
|
25
|
+
export const fetchAllPositions = adapter.fetchAllPositions;
|
|
26
|
+
export const fetchPositionDetail = adapter.fetchPositionDetail;
|
|
27
|
+
export const fetchDictionaries = adapter.fetchDictionaries;
|
|
28
|
+
export const listNotices = adapter.listNotices;
|
|
29
|
+
export const getNotice = adapter.getNotice;
|
|
30
|
+
export const findNoticesByQuestion = adapter.findNoticesByQuestion;
|
|
31
|
+
export const matchResume = adapter.matchResume;
|
|
32
|
+
export const checkResume = adapter.checkResume;
|
package/dist/geely.js
CHANGED
|
@@ -1,62 +1,34 @@
|
|
|
1
|
-
// 吉利汽车 (Geely Auto) —
|
|
2
|
-
//
|
|
3
|
-
// STATUS: stub-only. The careers domains do not resolve over public DNS,
|
|
4
|
-
// and the third-party ATS slugs (Greenhouse, Lever, Feishu, Moka) all return
|
|
5
|
-
// 404 or are unprovisioned. Public-facing recruiting appears to run only
|
|
6
|
-
// through WeChat / official-account channels.
|
|
1
|
+
// 吉利汽车 (Geely Auto) careers adapter — Moka SSR + AES-128-CBC pagination.
|
|
7
2
|
//
|
|
8
3
|
// ============================================================
|
|
9
|
-
//
|
|
4
|
+
// API DISCOVERY (probed 2026-05-16)
|
|
10
5
|
//
|
|
11
|
-
//
|
|
12
|
-
// https://
|
|
13
|
-
// https://hr.geely.com — 000 (no public DNS)
|
|
6
|
+
// `job.geely.com` is a CNAME that 302-redirects to a Moka tenant:
|
|
7
|
+
// https://app.mokahr.com/social-recruitment/geely/96123/
|
|
14
8
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// Moka: app.mokahr.com/social-recruitment/geely → 302 (slug unprovisioned)
|
|
9
|
+
// (The `198.18.x` IP that `job.geely.com` resolves to is an Alibaba-Cloud
|
|
10
|
+
// front; the actual upstream is `app.mokahr.com`.) The SSR HTML at that
|
|
11
|
+
// URL embeds the standard Moka `<input id="init-data" value="…">` blob
|
|
12
|
+
// containing the first page of jobs + aesIv for AES-128-CBC pagination.
|
|
20
13
|
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export async function listNotices() {
|
|
43
|
-
return { ok: false, source: SOURCE, message: STUB_MESSAGE, notices: [] };
|
|
44
|
-
}
|
|
45
|
-
export async function getNotice(noticeId) {
|
|
46
|
-
return { ok: false, source: SOURCE, message: STUB_MESSAGE, notice_id: noticeId };
|
|
47
|
-
}
|
|
48
|
-
export async function findNoticesByQuestion(question, _opts = {}) {
|
|
49
|
-
return { ok: false, source: SOURCE, question, message: STUB_MESSAGE, matches: [] };
|
|
50
|
-
}
|
|
51
|
-
export async function matchResume(text, _opts = {}) {
|
|
52
|
-
const { terms, cities } = extractResumeSignals(text ?? "");
|
|
53
|
-
return {
|
|
54
|
-
ok: false,
|
|
55
|
-
source: SOURCE,
|
|
56
|
-
extracted_terms: terms,
|
|
57
|
-
city_preferences: cities,
|
|
58
|
-
matches: [],
|
|
59
|
-
message: STUB_MESSAGE,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
export { extractResumeSignals, scoreOverlap };
|
|
14
|
+
// Same factory as `cli/src/moka.ts` (used by megvii / cambricon / etc.).
|
|
15
|
+
// Only the social-recruitment channel is published publicly — no
|
|
16
|
+
// campus-recruitment URL is linked from the Geely corporate site.
|
|
17
|
+
import { createAdapter } from "./moka.js";
|
|
18
|
+
const adapter = createAdapter({
|
|
19
|
+
orgSlug: "geely",
|
|
20
|
+
label: "Geely",
|
|
21
|
+
channels: [
|
|
22
|
+
{ siteId: 96123, kind: "social-recruitment", recruitType: "social" },
|
|
23
|
+
],
|
|
24
|
+
defaultRecruitType: "social",
|
|
25
|
+
});
|
|
26
|
+
export const searchPositions = adapter.searchPositions;
|
|
27
|
+
export const fetchAllPositions = adapter.fetchAllPositions;
|
|
28
|
+
export const fetchPositionDetail = adapter.fetchPositionDetail;
|
|
29
|
+
export const fetchDictionaries = adapter.fetchDictionaries;
|
|
30
|
+
export const listNotices = adapter.listNotices;
|
|
31
|
+
export const getNotice = adapter.getNotice;
|
|
32
|
+
export const findNoticesByQuestion = adapter.findNoticesByQuestion;
|
|
33
|
+
export const matchResume = adapter.matchResume;
|
|
34
|
+
export const checkResume = adapter.checkResume;
|
package/dist/index.js
CHANGED
|
@@ -51,7 +51,7 @@ import * as webank from "./webank.js";
|
|
|
51
51
|
import * as horizonrobotics from "./horizonrobotics.js";
|
|
52
52
|
import * as cambricon from "./cambricon.js";
|
|
53
53
|
import { memoryList, memoryGet, memorySet, memoryEvent, memoryClear, } from "./memory.js";
|
|
54
|
-
const VERSION = "0.7.
|
|
54
|
+
const VERSION = "0.7.3";
|
|
55
55
|
const HELP = `
|
|
56
56
|
job-pro — query Chinese big-tech campus recruiting from your terminal
|
|
57
57
|
(job.ha7ch.com)
|