job-pro 0.4.0 → 0.6.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.
- package/dist/antgroup.js +201 -0
- package/dist/byd.js +160 -0
- package/dist/feishu.js +446 -0
- package/dist/huawei.js +506 -0
- package/dist/index.js +37 -1
- package/dist/mihoyo.js +100 -0
- package/dist/minimax.js +32 -0
- package/dist/nio.js +24 -0
- package/dist/pdd.js +434 -0
- package/dist/pingan.js +462 -0
- package/dist/sensetime.js +186 -0
- package/dist/trip.js +365 -0
- package/dist/unitree.js +389 -0
- package/dist/weibo.js +135 -0
- package/package.json +2 -2
package/dist/antgroup.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Thin client for Ant Group's campus-recruiting portal at talent.antgroup.com.
|
|
2
|
+
//
|
|
3
|
+
// ============================================================
|
|
4
|
+
// API Discovery (probed 2026-05, JS bundle + network analysis):
|
|
5
|
+
//
|
|
6
|
+
// Portal URL: https://talent.antgroup.com/campus-list (public list view)
|
|
7
|
+
// https://talent.antgroup.com/campus-full-list (full list view)
|
|
8
|
+
// JS bundles: gw.alipayobjects.com/render/p/yuyan/180020010001257966/umi.6f081e74.js
|
|
9
|
+
// render.alipay.com/p/yuyan/180020010001257966/p__CampusRecruitment__CRList__index.*.async.js
|
|
10
|
+
// render.alipay.com/p/yuyan/180020010001257966/p__CampusRecruitment__CRFullList__index.*.async.js
|
|
11
|
+
// Gateway host: talent.antgroup.com (Spanner CDN/WAF, Alipay's proprietary gateway)
|
|
12
|
+
// Backend host: antwork-prod.antgroup-inc.cn (actual API server)
|
|
13
|
+
//
|
|
14
|
+
// ============================================================
|
|
15
|
+
// Endpoint inventory (extracted from JS bundle module 64588 + full UMI bundle):
|
|
16
|
+
//
|
|
17
|
+
// POST /api/campus/position/search — paginated job search
|
|
18
|
+
// POST /api/campus/position/detail — single position detail
|
|
19
|
+
// POST /api/campus/position/queryDept — dept tree for a position group
|
|
20
|
+
// POST /api/campus/positionGroup/queryBatchConfig — batch config
|
|
21
|
+
// POST /api/campus/positionGroup/queryBatchDetailById — batch detail
|
|
22
|
+
// POST /api/searchCondition/list — filter taxonomy (categories, cities, depts)
|
|
23
|
+
// POST /api/searchCondition/listPositionGroup
|
|
24
|
+
// POST /api/searchCondition/listTalentPlan
|
|
25
|
+
//
|
|
26
|
+
// Canonical position detail URL: /campus-position?positionId=<id>
|
|
27
|
+
//
|
|
28
|
+
// ============================================================
|
|
29
|
+
// AUTH STATUS — GATED (Alipay OAuth / buservice SDK):
|
|
30
|
+
//
|
|
31
|
+
// EVERY endpoint (including /api/campus/position/search and
|
|
32
|
+
// /api/searchCondition/list) requires an authenticated Alipay/Ant Group
|
|
33
|
+
// session. Without login, the backend returns:
|
|
34
|
+
//
|
|
35
|
+
// { "buserviceErrorCode": "USER_NOT_LOGIN",
|
|
36
|
+
// "buserviceErrorMsg": "https://pubbuservice.alipay.com/…" }
|
|
37
|
+
//
|
|
38
|
+
// The buservice middleware intercepts ALL routes as a catch-all auth gate
|
|
39
|
+
// before any controller logic runs. There is no guest/anonymous tier.
|
|
40
|
+
//
|
|
41
|
+
// The talent.antgroup.com Spanner gateway additionally returns 405 Method
|
|
42
|
+
// Not Allowed for POST requests that lack valid Alipay session cookies,
|
|
43
|
+
// preventing even the USER_NOT_LOGIN response from being seen in most cases.
|
|
44
|
+
// Direct calls to antwork-prod.antgroup-inc.cn reveal the auth error clearly.
|
|
45
|
+
//
|
|
46
|
+
// ============================================================
|
|
47
|
+
// CSRF / session flow (observed but INSUFFICIENT for anonymous access):
|
|
48
|
+
//
|
|
49
|
+
// GET /campus-list sets:
|
|
50
|
+
// ALIPAYJSESSIONID=<token>; domain=.antgroup.com
|
|
51
|
+
// _CHIPS-ALIPAYJSESSIONID=<same_token>; samesite=none; partitioned
|
|
52
|
+
// spanner=<signed_value>; path=/; secure
|
|
53
|
+
//
|
|
54
|
+
// These cookies are required for CORS (Access-Control-Allow-Credentials: true)
|
|
55
|
+
// but the buservice SDK then validates the session against Alipay's auth
|
|
56
|
+
// infrastructure — a simple GET-derived cookie has no authenticated user.
|
|
57
|
+
// Unlike Alibaba's portal (campus-talent.alibaba.com) which only needs an
|
|
58
|
+
// XSRF-TOKEN for public search, Ant Group's portal requires full Alipay OAuth.
|
|
59
|
+
//
|
|
60
|
+
// ============================================================
|
|
61
|
+
// Ant Group vs Alibaba — KEY DIFFERENCES:
|
|
62
|
+
//
|
|
63
|
+
// Portal: talent.antgroup.com vs campus-talent.alibaba.com
|
|
64
|
+
// Auth: Alipay OAuth (gated) vs XSRF-TOKEN only (public search works)
|
|
65
|
+
// CSRF: Not sufficient alone vs Sufficient for anonymous search
|
|
66
|
+
// Backend host: antwork-prod.antgroup-inc.cn vs campus-talent.alibaba.com
|
|
67
|
+
// Auth MW: buservice SDK (blocks all) vs Spring XSRF (only mutating ops)
|
|
68
|
+
//
|
|
69
|
+
// ============================================================
|
|
70
|
+
// FILTER TAXONOMY (from JS bundle, not verified against live API):
|
|
71
|
+
// channel values: "campus_group_official_site" (zh), "en_official_site" (en)
|
|
72
|
+
// searchCondition/list returns: searchItems with types "workCity", "category", "dept", "recruitType"
|
|
73
|
+
// Position fields: id, categoryName, workLocations, graduationTime, circleNames (BU)
|
|
74
|
+
//
|
|
75
|
+
// ============================================================
|
|
76
|
+
// ---- PositionSummary field mapping (Ant Group → canonical) ----
|
|
77
|
+
// post_id ← item.id (stringified)
|
|
78
|
+
// title ← item.name
|
|
79
|
+
// project ← item.categoryName ?? "" (e.g. "技术类", "产品类")
|
|
80
|
+
// recruit_label ← item.recruitType ?? "" (e.g. "实习生", "校招生")
|
|
81
|
+
// bgs ← item.circleNames?.[0] ?? "" (BU / business unit)
|
|
82
|
+
// work_cities ← item.workLocations?.join(" / ") ?? ""
|
|
83
|
+
// apply_url ← https://talent.antgroup.com/campus-position?positionId=<id>
|
|
84
|
+
import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
|
|
85
|
+
export { extractResumeSignals, scoreOverlap, checkResume };
|
|
86
|
+
const PORTAL_ROOT = "https://talent.antgroup.com";
|
|
87
|
+
const CAMPUS_PAGE = `${PORTAL_ROOT}/campus-list`;
|
|
88
|
+
const DETAIL_PAGE = (id) => `${PORTAL_ROOT}/campus-position?positionId=${encodeURIComponent(String(id))}`;
|
|
89
|
+
// ---------- stub reason constant ----------
|
|
90
|
+
const STUB_MESSAGE = "Ant Group (talent.antgroup.com): all API endpoints require Alipay OAuth login. " +
|
|
91
|
+
"POST /api/campus/position/search returns buserviceErrorCode=USER_NOT_LOGIN for " +
|
|
92
|
+
"unauthenticated requests. The Spanner CDN gateway additionally returns HTTP 405 " +
|
|
93
|
+
"for POST requests lacking a valid Alipay session cookie. No anonymous/guest tier exists. " +
|
|
94
|
+
"To use this portal, the user must log in at talent.antgroup.com with an Alipay account " +
|
|
95
|
+
"and supply a valid ALIPAYJSESSIONID cookie.";
|
|
96
|
+
// ---------- searchPositions (stub) ----------
|
|
97
|
+
export async function searchPositions(opts = {}) {
|
|
98
|
+
const pageSize = Math.max(1, Math.min(100, opts.pageSize ?? 20));
|
|
99
|
+
const page = Math.max(1, opts.page ?? 1);
|
|
100
|
+
const channel = opts.channel ?? "campus_group_official_site";
|
|
101
|
+
const query = {
|
|
102
|
+
pageIndex: page,
|
|
103
|
+
pageSize,
|
|
104
|
+
channel,
|
|
105
|
+
language: "zh",
|
|
106
|
+
};
|
|
107
|
+
if (opts.keyword?.trim())
|
|
108
|
+
query.keyword = opts.keyword.trim().slice(0, 60);
|
|
109
|
+
if (opts.category)
|
|
110
|
+
query.category = opts.category;
|
|
111
|
+
if (opts.region)
|
|
112
|
+
query.region = opts.region;
|
|
113
|
+
if (opts.deptCode)
|
|
114
|
+
query.deptCode = opts.deptCode;
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
source: PORTAL_ROOT,
|
|
118
|
+
message: STUB_MESSAGE,
|
|
119
|
+
query,
|
|
120
|
+
page,
|
|
121
|
+
page_size: pageSize,
|
|
122
|
+
total: null,
|
|
123
|
+
positions: [],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// ---------- fetchAllPositions (stub) ----------
|
|
127
|
+
export async function fetchAllPositions(opts = {}) {
|
|
128
|
+
return {
|
|
129
|
+
ok: false,
|
|
130
|
+
source: PORTAL_ROOT,
|
|
131
|
+
message: STUB_MESSAGE,
|
|
132
|
+
fetched: 0,
|
|
133
|
+
total: null,
|
|
134
|
+
positions: [],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// ---------- fetchPositionDetail (stub) ----------
|
|
138
|
+
export async function fetchPositionDetail(postId) {
|
|
139
|
+
const id = (postId ?? "").trim();
|
|
140
|
+
if (!id) {
|
|
141
|
+
return {
|
|
142
|
+
ok: false,
|
|
143
|
+
source: PORTAL_ROOT,
|
|
144
|
+
message: "post_id is required",
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
ok: false,
|
|
149
|
+
source: PORTAL_ROOT,
|
|
150
|
+
message: STUB_MESSAGE,
|
|
151
|
+
post_id: id,
|
|
152
|
+
apply_url: DETAIL_PAGE(id),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// ---------- fetchDictionaries (stub) ----------
|
|
156
|
+
export async function fetchDictionaries() {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
source: PORTAL_ROOT,
|
|
160
|
+
message: STUB_MESSAGE,
|
|
161
|
+
note: "Filter taxonomy (categories, cities, depts) is served via POST /api/searchCondition/list " +
|
|
162
|
+
"with body {channel:'campus_group_official_site', language:'zh'}. " +
|
|
163
|
+
"Response shape: { searchItems: [{type:'workCity'|'category'|'dept'|'recruitType', items:[{label,value}]}] }. " +
|
|
164
|
+
"All require Alipay login.",
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// ---------- notices (stub) ----------
|
|
168
|
+
export async function listNotices() {
|
|
169
|
+
return {
|
|
170
|
+
ok: false,
|
|
171
|
+
source: PORTAL_ROOT,
|
|
172
|
+
message: "Ant Group: no public notices endpoint",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
export async function getNotice(_id) {
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
source: PORTAL_ROOT,
|
|
179
|
+
message: "Ant Group: no public notices endpoint",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
export async function findNoticesByQuestion(_question, _opts = {}) {
|
|
183
|
+
return {
|
|
184
|
+
ok: false,
|
|
185
|
+
source: PORTAL_ROOT,
|
|
186
|
+
message: "Ant Group: no public notices endpoint",
|
|
187
|
+
matches: [],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// ---------- matchResume (stub) ----------
|
|
191
|
+
export async function matchResume(text, opts = {}) {
|
|
192
|
+
const { terms, cities } = extractResumeSignals(text ?? "");
|
|
193
|
+
return {
|
|
194
|
+
ok: false,
|
|
195
|
+
source: PORTAL_ROOT,
|
|
196
|
+
message: STUB_MESSAGE,
|
|
197
|
+
extracted_terms: terms,
|
|
198
|
+
city_preferences: cities,
|
|
199
|
+
matches: [],
|
|
200
|
+
};
|
|
201
|
+
}
|
package/dist/byd.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Thin client stub for BYD (比亚迪) campus-recruiting portal at job.byd.com.
|
|
2
|
+
//
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Endpoint discovery (probed 2026-05, JS bundle app.e46eb97b.js +
|
|
5
|
+
// chunk-e8fe.d262cda1.js, chunk-ac75.7dee0692.js, chunk-a7e5.62aed375.js,
|
|
6
|
+
// chunk-76ac.cedb4013.js, chunk-dbeb.0075e53e.js):
|
|
7
|
+
//
|
|
8
|
+
// Portal entry:
|
|
9
|
+
// https://job.byd.com/ → redirects to https://job.byd.com/portal/pc/
|
|
10
|
+
// https://careers.byd.com/ → Vite/Vue marketing page (static, no job listings)
|
|
11
|
+
// https://job.byd.com/portal/pc/ → main Vue SPA (webpack, ElementUI)
|
|
12
|
+
//
|
|
13
|
+
// Axios instance (t3Un module in app.e46eb97b.js):
|
|
14
|
+
// baseURL = "/portal/api"
|
|
15
|
+
// Interceptor: adds header Authorization: "bearer <token>" from Vuex store.
|
|
16
|
+
// Code 4001 → "Token无效或已过期: Not Authenticated" (auto-redirect to login).
|
|
17
|
+
//
|
|
18
|
+
// Campus-related API endpoints found in JS bundles:
|
|
19
|
+
// POST /portal/api/school/queryJobList → campus job list
|
|
20
|
+
// POST /portal/api/position/queryList → position list (also skiller/social)
|
|
21
|
+
// POST /portal/api/position/queryDetail → position detail
|
|
22
|
+
// POST /portal/api/other-info/notice/query-list → campus notices
|
|
23
|
+
// POST /portal/api/position/schedule/query-list → campus schedule / timeline
|
|
24
|
+
// GET /portal/api/siteInfo/faq → FAQ
|
|
25
|
+
// POST /portal/api/common/queryCodeTree → code dictionary
|
|
26
|
+
//
|
|
27
|
+
// All endpoints probed 2026-05: EVERY request returns:
|
|
28
|
+
// HTTP 200, body: {"code":4001,"timestamp":...,"msg":"Token无效或已过期: Not Authenticated"}
|
|
29
|
+
//
|
|
30
|
+
// Auth model:
|
|
31
|
+
// Requires a JWT bearer token obtained through BYD account login
|
|
32
|
+
// (POST /portal/api/account/login, then GET /portal/api/account/user-info).
|
|
33
|
+
// There is NO public/anonymous browsing API — even the FAQ and code-tree
|
|
34
|
+
// endpoints are gated behind a valid token.
|
|
35
|
+
//
|
|
36
|
+
// careers.byd.com investigation:
|
|
37
|
+
// careers.byd.com is an internationalised marketing SPA (Vite + Vue 3).
|
|
38
|
+
// Its BydPage-6104aa3e.js uses baseURL "/global-portal/api" with two
|
|
39
|
+
// known endpoints:
|
|
40
|
+
// GET /global-portal-api/global-material/getGlobalMaterial → 404
|
|
41
|
+
// GET /global-portal-api/global-country/getCountryNetwork → 404
|
|
42
|
+
// The site is a static landing page that links to job.byd.com; it does
|
|
43
|
+
// not expose any independent job-search API.
|
|
44
|
+
//
|
|
45
|
+
// ============================================================
|
|
46
|
+
// Summary: BYD has no publicly accessible campus-job search API.
|
|
47
|
+
// All API calls require a logged-in user JWT.
|
|
48
|
+
// This adapter is an honest stub — every function returns ok:false with
|
|
49
|
+
// an informative message. It will be upgraded once an authenticated
|
|
50
|
+
// (scrape-friendly) path is identified.
|
|
51
|
+
//
|
|
52
|
+
// ============================================================
|
|
53
|
+
// PositionSummary field mapping (BYD → canonical, documented for future use):
|
|
54
|
+
// post_id ← item.positionId or item.id (string)
|
|
55
|
+
// title ← item.positionName (e.g. "校招-软件开发工程师")
|
|
56
|
+
// project ← item.positionTypeName (职位类型, e.g. "研发")
|
|
57
|
+
// recruit_label ← item.recruitTypeName (e.g. "应届生" / "实习生")
|
|
58
|
+
// bgs ← "" (not exposed in API)
|
|
59
|
+
// work_cities ← item.workPlace or item.city (free-text Chinese city string)
|
|
60
|
+
// apply_url ← https://job.byd.com/portal/pc/school/schoolPositionApply
|
|
61
|
+
// ?positionId={id}
|
|
62
|
+
//
|
|
63
|
+
// ============================================================
|
|
64
|
+
import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
|
|
65
|
+
export { checkResume };
|
|
66
|
+
const SOURCE = "job.byd.com";
|
|
67
|
+
const CAMPUS_PAGE = "https://job.byd.com/portal/pc/school/home";
|
|
68
|
+
// ---- stub reason ----
|
|
69
|
+
const STUB_REASON = "BYD job.byd.com: all API endpoints require a valid JWT bearer token " +
|
|
70
|
+
"(code 4001 — Token无效或已过期). No public/anonymous job search API exists. " +
|
|
71
|
+
"Visit https://job.byd.com/portal/pc/school/home to browse positions after login.";
|
|
72
|
+
// ---- searchPositions ----
|
|
73
|
+
export async function searchPositions(_opts = {}) {
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
source: SOURCE,
|
|
77
|
+
message: STUB_REASON,
|
|
78
|
+
campus_page: CAMPUS_PAGE,
|
|
79
|
+
positions: [],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// ---- fetchAllPositions ----
|
|
83
|
+
export async function fetchAllPositions(_opts = {}) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
source: SOURCE,
|
|
87
|
+
message: STUB_REASON,
|
|
88
|
+
campus_page: CAMPUS_PAGE,
|
|
89
|
+
fetched: 0,
|
|
90
|
+
positions: [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// ---- fetchPositionDetail ----
|
|
94
|
+
export async function fetchPositionDetail(_postId) {
|
|
95
|
+
return { ok: false, source: SOURCE, message: STUB_REASON };
|
|
96
|
+
}
|
|
97
|
+
// ---- fetchDictionaries ----
|
|
98
|
+
export async function fetchDictionaries() {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
source: SOURCE,
|
|
102
|
+
message: STUB_REASON,
|
|
103
|
+
note: "BYD: no public filter-taxonomy endpoint. " +
|
|
104
|
+
"POST /portal/api/common/queryCodeTree returns 4001 without a token.",
|
|
105
|
+
known_endpoints: [
|
|
106
|
+
"POST /portal/api/school/queryJobList (campus job list — auth required)",
|
|
107
|
+
"POST /portal/api/position/queryList (position list — auth required)",
|
|
108
|
+
"POST /portal/api/position/queryDetail (position detail — auth required)",
|
|
109
|
+
"POST /portal/api/other-info/notice/query-list (notices — auth required)",
|
|
110
|
+
"POST /portal/api/position/schedule/query-list (campus schedule — auth required)",
|
|
111
|
+
"GET /portal/api/siteInfo/faq (FAQ — auth required)",
|
|
112
|
+
"POST /portal/api/common/queryCodeTree (code tree — auth required)",
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// ---- listNotices ----
|
|
117
|
+
export async function listNotices() {
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
source: SOURCE,
|
|
121
|
+
message: "BYD: notices endpoint (POST /portal/api/other-info/notice/query-list) requires authentication.",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// ---- getNotice ----
|
|
125
|
+
export async function getNotice(_id) {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
source: SOURCE,
|
|
129
|
+
message: "BYD: no public notices endpoint (auth required).",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// ---- findNoticesByQuestion ----
|
|
133
|
+
export async function findNoticesByQuestion(_question, _opts = {}) {
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
source: SOURCE,
|
|
137
|
+
message: "BYD: no public notices endpoint (auth required).",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// ---- matchResume ----
|
|
141
|
+
// Resume matching is best-effort using extractResumeSignals/scoreOverlap from
|
|
142
|
+
// tencent.ts, but since the position listing API is gated, we can only return
|
|
143
|
+
// a stub with the extracted signals and a pointer to the campus page.
|
|
144
|
+
export async function matchResume(text, opts = {}) {
|
|
145
|
+
// Extract signals so the caller knows what was parsed from the resume
|
|
146
|
+
const { terms, cities } = extractResumeSignals(text ?? "");
|
|
147
|
+
void opts; // unused until API becomes accessible
|
|
148
|
+
return {
|
|
149
|
+
ok: false,
|
|
150
|
+
source: SOURCE,
|
|
151
|
+
message: "BYD: cannot search positions — API requires authentication. " +
|
|
152
|
+
`Extracted resume signals: [${terms.slice(0, 10).join(", ")}]. ` +
|
|
153
|
+
"Visit the campus page to search manually.",
|
|
154
|
+
campus_page: CAMPUS_PAGE,
|
|
155
|
+
extracted_terms: terms,
|
|
156
|
+
city_preferences: cities,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// ---- re-export helpers so the tencent resume signals are accessible ----
|
|
160
|
+
export { extractResumeSignals, scoreOverlap };
|