linkedin-api-voyager 1.0.0 → 1.1.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/lib/account.d.ts +18 -0
- package/lib/account.js +98 -0
- package/lib/company.d.ts +1 -0
- package/lib/company.js +37 -0
- package/lib/config.d.ts +20 -0
- package/lib/config.js +100 -0
- package/lib/index.js +22 -0
- package/lib/linkedin.js +1 -0
- package/lib/posts.d.ts +2 -0
- package/lib/posts.js +57 -0
- package/lib/search.d.ts +26 -0
- package/lib/search.js +133 -0
- package/lib/utils.d.ts +10 -0
- package/lib/utils.js +193 -0
- package/package.json +8 -2
- package/src/account.ts +0 -116
- package/src/company.ts +0 -33
- package/src/config.ts +0 -109
- package/src/posts.ts +0 -71
- package/src/search.ts +0 -183
- package/src/utils.ts +0 -213
- package/tsconfig.json +0 -10
- /package/{src/index.ts → lib/index.d.ts} +0 -0
- /package/{src/linkedin.ts → lib/linkedin.d.ts} +0 -0
package/lib/account.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const getProfile: (identifier: string) => Promise<{
|
|
2
|
+
publicIdentifier: any;
|
|
3
|
+
firstName: any;
|
|
4
|
+
lastName: any;
|
|
5
|
+
fullName: string;
|
|
6
|
+
birthDate: string | null;
|
|
7
|
+
profilePicture: string | null;
|
|
8
|
+
backgroundPicture: string | null;
|
|
9
|
+
location: {
|
|
10
|
+
country: any;
|
|
11
|
+
city: any;
|
|
12
|
+
};
|
|
13
|
+
address: any;
|
|
14
|
+
industry: any;
|
|
15
|
+
headline: any;
|
|
16
|
+
summary: any;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const getProfissionalExperiences: (identifier: string) => Promise<any[]>;
|
package/lib/account.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getProfissionalExperiences = exports.getProfile = void 0;
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
const getProfile = (identifier) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
var _a, _b;
|
|
17
|
+
const response = yield (0, config_1.fetchData)(`/identity/profiles/${identifier}/profileView`);
|
|
18
|
+
const data = response.data;
|
|
19
|
+
const dataResult = response === null || response === void 0 ? void 0 : response.included;
|
|
20
|
+
const getEntityByUrn = (urn) => dataResult.find((item) => item.entityUrn === urn);
|
|
21
|
+
const keyProfile = getEntityByUrn(data === null || data === void 0 ? void 0 : data["*profile"]);
|
|
22
|
+
if (!keyProfile)
|
|
23
|
+
throw new Error("Key profile not found");
|
|
24
|
+
const miniProfile = getEntityByUrn(keyProfile === null || keyProfile === void 0 ? void 0 : keyProfile["*miniProfile"]);
|
|
25
|
+
if (!miniProfile)
|
|
26
|
+
throw new Error("Mini profile not found");
|
|
27
|
+
const profile = {
|
|
28
|
+
// id_urn: keyProfile.entityUrn?.split("urn:li:fs_profile:")[1] || null,
|
|
29
|
+
publicIdentifier: (miniProfile === null || miniProfile === void 0 ? void 0 : miniProfile.publicIdentifier) || null,
|
|
30
|
+
firstName: keyProfile.firstName || null,
|
|
31
|
+
lastName: keyProfile.lastName || null,
|
|
32
|
+
fullName: `${keyProfile.firstName || ""} ${keyProfile.lastName || ""}`,
|
|
33
|
+
birthDate: keyProfile.birthDate
|
|
34
|
+
? JSON.stringify({
|
|
35
|
+
month: keyProfile.birthDate.month,
|
|
36
|
+
day: keyProfile.birthDate.day,
|
|
37
|
+
})
|
|
38
|
+
: null,
|
|
39
|
+
profilePicture: miniProfile.picture
|
|
40
|
+
? `${miniProfile.picture.rootUrl}${(_a = miniProfile.picture.artifacts[miniProfile.picture.artifacts.length - 1]) === null || _a === void 0 ? void 0 : _a.fileIdentifyingUrlPathSegment}`
|
|
41
|
+
: null,
|
|
42
|
+
backgroundPicture: miniProfile.backgroundImage
|
|
43
|
+
? `${miniProfile.backgroundImage.rootUrl}${(_b = miniProfile.backgroundImage.artifacts[miniProfile.backgroundImage.artifacts.length - 1]) === null || _b === void 0 ? void 0 : _b.fileIdentifyingUrlPathSegment}`
|
|
44
|
+
: null,
|
|
45
|
+
location: {
|
|
46
|
+
country: keyProfile.locationName || null,
|
|
47
|
+
city: keyProfile.geoLocationName || null,
|
|
48
|
+
},
|
|
49
|
+
address: keyProfile.address || null,
|
|
50
|
+
industry: keyProfile.industryName || null,
|
|
51
|
+
headline: keyProfile.headline || null,
|
|
52
|
+
summary: keyProfile.summary || null,
|
|
53
|
+
};
|
|
54
|
+
return profile;
|
|
55
|
+
});
|
|
56
|
+
exports.getProfile = getProfile;
|
|
57
|
+
const getProfissionalExperiences = (identifier) => __awaiter(void 0, void 0, void 0, function* () {
|
|
58
|
+
const response = yield (0, config_1.fetchData)(`/identity/profiles/${identifier}/positions`);
|
|
59
|
+
let { data, included } = response;
|
|
60
|
+
const elements = data["*elements"];
|
|
61
|
+
// Usar a nova função para resolver referências automaticamente
|
|
62
|
+
const dataExperiences = (0, utils_1.extractDataWithReferences)(elements, included);
|
|
63
|
+
// Extrair campos específicos do included
|
|
64
|
+
const extraFields = (0, utils_1.extractFieldsFromIncluded)(included, ["universalName"]);
|
|
65
|
+
// Mapeamento de campos
|
|
66
|
+
const fieldsMap = {
|
|
67
|
+
id: "entityUrn",
|
|
68
|
+
title: "title",
|
|
69
|
+
companyName: "company.miniCompany.name",
|
|
70
|
+
companyUrn: "companyUrn",
|
|
71
|
+
companyEmployeeCount: "company.employeeCountRange",
|
|
72
|
+
companyIndustries: "company.miniCompany.industries",
|
|
73
|
+
description: "description",
|
|
74
|
+
location: "locationName",
|
|
75
|
+
geoLocation: "geoLocationName",
|
|
76
|
+
timePeriod: "timePeriod",
|
|
77
|
+
startDate: "timePeriod.startDate",
|
|
78
|
+
endDate: "timePeriod.endDate",
|
|
79
|
+
};
|
|
80
|
+
// Aplicar mapeamento aos dados resolvidos
|
|
81
|
+
const mappedExperiences = (0, utils_1.extractFields)(dataExperiences, fieldsMap);
|
|
82
|
+
// Associar campos extras
|
|
83
|
+
const experiencesWithExtras = (0, utils_1.mergeExtraFields)(mappedExperiences, extraFields, "companyUrn");
|
|
84
|
+
// Ordenar: sem endDate (ativo) primeiro, depois do mais recente ao mais antigo
|
|
85
|
+
return experiencesWithExtras.sort((a, b) => {
|
|
86
|
+
if (!a.endDate && b.endDate)
|
|
87
|
+
return -1;
|
|
88
|
+
if (a.endDate && !b.endDate)
|
|
89
|
+
return 1;
|
|
90
|
+
if (!a.endDate && !b.endDate)
|
|
91
|
+
return 0;
|
|
92
|
+
const yearDiff = (b.endDate.year || 0) - (a.endDate.year || 0);
|
|
93
|
+
if (yearDiff !== 0)
|
|
94
|
+
return yearDiff;
|
|
95
|
+
return (b.endDate.month || 0) - (a.endDate.month || 0);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
exports.getProfissionalExperiences = getProfissionalExperiences;
|
package/lib/company.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getCompany: (identifier: string) => Promise<any>;
|
package/lib/company.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getCompany = void 0;
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
const getCompany = (identifier) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
const response = yield (0, config_1.fetchData)(`/organization/companies?decorationId=com.linkedin.voyager.deco.organization.web.WebFullCompanyMain-12&q=universalName&universalName=${identifier}`);
|
|
17
|
+
const data = (0, utils_1.extractDataWithReferences)(response.data["*elements"], response.included);
|
|
18
|
+
const fieldsMap = {
|
|
19
|
+
id: "entityUrn",
|
|
20
|
+
name: "name",
|
|
21
|
+
description: "description",
|
|
22
|
+
username: "universalName",
|
|
23
|
+
companyPageUrl: "companyPageUrl",
|
|
24
|
+
staffCount: "staffCount",
|
|
25
|
+
url: "url",
|
|
26
|
+
companyIndustries: "*companyIndustries[0].localizedName",
|
|
27
|
+
location: "locationName",
|
|
28
|
+
jobSearchPageUrl: "jobSearchPageUrl",
|
|
29
|
+
phone: "phone",
|
|
30
|
+
followerCount: "followingInfo.followerCount",
|
|
31
|
+
backgroundCoverImage: "backgroundCoverImage.image",
|
|
32
|
+
logo: "logo.image",
|
|
33
|
+
permissions: "permissions",
|
|
34
|
+
};
|
|
35
|
+
return (0, utils_1.extractFields)(data, fieldsMap)[0];
|
|
36
|
+
});
|
|
37
|
+
exports.getCompany = getCompany;
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const COOKIE_FILE_PATH = "linkedin_cookies.json";
|
|
2
|
+
export declare const API_BASE_URL = "https://www.linkedin.com/voyager/api";
|
|
3
|
+
export declare const AUTH_BASE_URL = "https://www.linkedin.com";
|
|
4
|
+
interface LinkedInCookies {
|
|
5
|
+
JSESSIONID: string;
|
|
6
|
+
li_at: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const saveCookies: (JSESSIONID: string, li_at: string) => Promise<void>;
|
|
10
|
+
export declare const loadCookies: () => Promise<LinkedInCookies | null>;
|
|
11
|
+
export declare const Client: (providedCookies?: {
|
|
12
|
+
JSESSIONID: string;
|
|
13
|
+
li_at: string;
|
|
14
|
+
}) => Promise<ReturnType<typeof api>>;
|
|
15
|
+
declare const api: ({ JSESSIONID, li_at }: {
|
|
16
|
+
li_at: string;
|
|
17
|
+
JSESSIONID: number;
|
|
18
|
+
}) => import("axios").AxiosInstance;
|
|
19
|
+
export declare const fetchData: (endpoint: string) => Promise<any>;
|
|
20
|
+
export {};
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.fetchData = exports.Client = exports.loadCookies = exports.saveCookies = exports.AUTH_BASE_URL = exports.API_BASE_URL = exports.COOKIE_FILE_PATH = void 0;
|
|
13
|
+
const fs = require("fs-extra");
|
|
14
|
+
const axios_1 = require("axios");
|
|
15
|
+
exports.COOKIE_FILE_PATH = "linkedin_cookies.json";
|
|
16
|
+
exports.API_BASE_URL = "https://www.linkedin.com/voyager/api";
|
|
17
|
+
exports.AUTH_BASE_URL = "https://www.linkedin.com";
|
|
18
|
+
// Função para salvar cookies no arquivo JSON
|
|
19
|
+
const saveCookies = (JSESSIONID, li_at) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
try {
|
|
21
|
+
const cookies = {
|
|
22
|
+
JSESSIONID,
|
|
23
|
+
li_at,
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
};
|
|
26
|
+
yield fs.ensureFile(exports.COOKIE_FILE_PATH);
|
|
27
|
+
yield fs.writeJson(exports.COOKIE_FILE_PATH, cookies, { spaces: 2 });
|
|
28
|
+
console.log(`Cookies salvos em: ${exports.COOKIE_FILE_PATH}`);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("Erro ao salvar cookies:", error);
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
exports.saveCookies = saveCookies;
|
|
36
|
+
// Função para carregar cookies do arquivo JSON
|
|
37
|
+
const loadCookies = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
try {
|
|
39
|
+
const exists = yield fs.pathExists(exports.COOKIE_FILE_PATH);
|
|
40
|
+
if (!exists) {
|
|
41
|
+
console.log("Arquivo de cookies não encontrado");
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const cookies = yield fs.readJson(exports.COOKIE_FILE_PATH);
|
|
45
|
+
// Verificar se os cookies têm a estrutura esperada
|
|
46
|
+
if (!cookies.JSESSIONID || !cookies.li_at) {
|
|
47
|
+
console.log("Cookies inválidos encontrados no arquivo");
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
console.log(`Cookies carregados de: ${exports.COOKIE_FILE_PATH}`);
|
|
51
|
+
return cookies;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error("Erro ao carregar cookies:", error);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
exports.loadCookies = loadCookies;
|
|
59
|
+
// Função para criar cliente com cookies automáticos
|
|
60
|
+
const Client = (providedCookies) => __awaiter(void 0, void 0, void 0, function* () {
|
|
61
|
+
let cookiesToUse;
|
|
62
|
+
const savedCookies = yield (0, exports.loadCookies)();
|
|
63
|
+
if (savedCookies) {
|
|
64
|
+
cookiesToUse = {
|
|
65
|
+
JSESSIONID: savedCookies.JSESSIONID,
|
|
66
|
+
li_at: savedCookies.li_at,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
if (providedCookies) {
|
|
71
|
+
yield (0, exports.saveCookies)(providedCookies.JSESSIONID, providedCookies.li_at);
|
|
72
|
+
cookiesToUse = providedCookies;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
throw new Error("Nenhum cookie válido fornecido");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return api({
|
|
79
|
+
JSESSIONID: parseInt(cookiesToUse.JSESSIONID),
|
|
80
|
+
li_at: cookiesToUse.li_at,
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
exports.Client = Client;
|
|
84
|
+
const api = ({ JSESSIONID, li_at }) => {
|
|
85
|
+
return axios_1.default.create({
|
|
86
|
+
baseURL: exports.API_BASE_URL,
|
|
87
|
+
headers: {
|
|
88
|
+
"accept-language": "pt-BR,pt;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5",
|
|
89
|
+
accept: "application/vnd.linkedin.normalized+json+2.1",
|
|
90
|
+
cookie: `li_at=${li_at}; JSESSIONID="ajax:${JSESSIONID}"`,
|
|
91
|
+
"csrf-token": `ajax:${JSESSIONID}`,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
const fetchData = (endpoint) => __awaiter(void 0, void 0, void 0, function* () {
|
|
96
|
+
const api = yield (0, exports.Client)();
|
|
97
|
+
const response = yield api.get(endpoint);
|
|
98
|
+
return response.data;
|
|
99
|
+
});
|
|
100
|
+
exports.fetchData = fetchData;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./account"), exports);
|
|
18
|
+
__exportStar(require("./company"), exports);
|
|
19
|
+
__exportStar(require("./posts"), exports);
|
|
20
|
+
__exportStar(require("./search"), exports);
|
|
21
|
+
__exportStar(require("./utils"), exports);
|
|
22
|
+
__exportStar(require("./config"), exports);
|
package/lib/linkedin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
package/lib/posts.d.ts
ADDED
package/lib/posts.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getPosts = exports.getCommentsByPostUrl = void 0;
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const utils_1 = require("./utils");
|
|
15
|
+
const getCommentsByPostUrl = (url_1, ...args_1) => __awaiter(void 0, [url_1, ...args_1], void 0, function* (url, start = 0, limit = 50, accumulatedComments = []) {
|
|
16
|
+
var _a, _b, _c, _d, _e;
|
|
17
|
+
const postID = (_a = url.match(/activity-(\d+)/)) === null || _a === void 0 ? void 0 : _a[1];
|
|
18
|
+
const response = yield (0, config_1.fetchData)(`/graphql?includeWebMetadata=false&queryId=voyagerSocialDashComments.95ed44bc87596acce7c460c70934d0ff&variables=(count:${limit},start:${start},numReplies:1,socialDetailUrn:urn%3Ali%3Afsd_socialDetail%3A%28urn%3Ali%3Aactivity%${postID}%2Curn%3Ali%3Aactivity%3A${postID}%2Curn%3Ali%3AhighlightedReply%3A-%29,sortOrder:RELEVANCE)`);
|
|
19
|
+
const elements = (_d = (_c = (_b = response.data) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.socialDashCommentsBySocialDetail) === null || _d === void 0 ? void 0 : _d["*elements"];
|
|
20
|
+
// Se não há elementos, retorna os comentários acumulados
|
|
21
|
+
if (!elements || elements.length === 0) {
|
|
22
|
+
// console.log(
|
|
23
|
+
// "✅ Busca finalizada. Total de comentários:",
|
|
24
|
+
// accumulatedComments.length
|
|
25
|
+
// );
|
|
26
|
+
return accumulatedComments;
|
|
27
|
+
}
|
|
28
|
+
const data = ((_e = response.included) === null || _e === void 0 ? void 0 : _e.filter((item) => elements.includes(item.entityUrn))) || [];
|
|
29
|
+
// Mapeamento melhorado dos campos
|
|
30
|
+
const fieldsMap = {
|
|
31
|
+
id: "entityUrn",
|
|
32
|
+
createdAt: "createdAt",
|
|
33
|
+
isAuthor: "commenter.author",
|
|
34
|
+
name: "commenter.title.text",
|
|
35
|
+
headline: "commenter.subtitle",
|
|
36
|
+
profileUrl: "commenter.navigationUrl",
|
|
37
|
+
comment: "commentary.text",
|
|
38
|
+
permalink: "permalink",
|
|
39
|
+
image: "commenter.image.attributes.0.detailData.nonEntityProfilePicture.vectorImage",
|
|
40
|
+
};
|
|
41
|
+
const currentComments = (0, utils_1.extractFields)(data, fieldsMap);
|
|
42
|
+
const allComments = [...accumulatedComments, ...currentComments];
|
|
43
|
+
// console.log(
|
|
44
|
+
// `🔍 Encontrados ${elements.length} comentários (Total: ${allComments.length})`
|
|
45
|
+
// );
|
|
46
|
+
// Continua a busca se há mais elementos
|
|
47
|
+
if (elements.length > 0) {
|
|
48
|
+
return yield (0, exports.getCommentsByPostUrl)(url, start + elements.length, limit, allComments);
|
|
49
|
+
}
|
|
50
|
+
// Se retornou menos que o limite, chegou ao fim
|
|
51
|
+
return allComments;
|
|
52
|
+
});
|
|
53
|
+
exports.getCommentsByPostUrl = getCommentsByPostUrl;
|
|
54
|
+
const getPosts = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
|
+
return [];
|
|
56
|
+
});
|
|
57
|
+
exports.getPosts = getPosts;
|
package/lib/search.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SearchParams {
|
|
2
|
+
count?: string;
|
|
3
|
+
filters?: string;
|
|
4
|
+
origin?: string;
|
|
5
|
+
q?: string;
|
|
6
|
+
start?: number;
|
|
7
|
+
queryContext?: string;
|
|
8
|
+
[key: string]: any;
|
|
9
|
+
}
|
|
10
|
+
export interface SearchElement {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
export interface SearchDataElement {
|
|
14
|
+
elements: SearchElement[];
|
|
15
|
+
extendedElements?: SearchElement[];
|
|
16
|
+
}
|
|
17
|
+
export interface SearchResponse {
|
|
18
|
+
data: {
|
|
19
|
+
elements: SearchDataElement[];
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface SearchOptions {
|
|
23
|
+
limit?: number;
|
|
24
|
+
results?: SearchElement[];
|
|
25
|
+
}
|
|
26
|
+
export declare const search: (params: SearchParams, options?: SearchOptions) => Promise<SearchElement[]>;
|
package/lib/search.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.search = void 0;
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
// Constantes
|
|
15
|
+
const MAX_SEARCH_COUNT = 25;
|
|
16
|
+
const MAX_REPEATED_REQUESTS = 40;
|
|
17
|
+
// Função utilitária para criar query string
|
|
18
|
+
const createQueryString = (params) => {
|
|
19
|
+
return Object.entries(params)
|
|
20
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
21
|
+
.join("&");
|
|
22
|
+
};
|
|
23
|
+
// Função utilitária para fazer fetch (assumindo que existe uma função _fetch no client)
|
|
24
|
+
const fetchData = (endpoint) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const api = yield (0, config_1.Client)();
|
|
26
|
+
const response = yield api.get(endpoint);
|
|
27
|
+
return response.data;
|
|
28
|
+
});
|
|
29
|
+
// Função principal de busca
|
|
30
|
+
const search = (params_1, ...args_1) => __awaiter(void 0, [params_1, ...args_1], void 0, function* (params, options = {}) {
|
|
31
|
+
const { limit, results = [] } = options;
|
|
32
|
+
// Determinar o count baseado no limite
|
|
33
|
+
const count = limit && limit <= MAX_SEARCH_COUNT ? limit : MAX_SEARCH_COUNT;
|
|
34
|
+
// Parâmetros padrão
|
|
35
|
+
const defaultParams = {
|
|
36
|
+
count: count.toString(),
|
|
37
|
+
filters: "List()",
|
|
38
|
+
origin: "GLOBAL_SEARCH_HEADER",
|
|
39
|
+
q: "all",
|
|
40
|
+
start: results.length,
|
|
41
|
+
queryContext: "List(spellCorrectionEnabled->true,relatedSearchesEnabled->true,kcardTypes->PROFILE|COMPANY)",
|
|
42
|
+
};
|
|
43
|
+
// Mesclar parâmetros padrão com os fornecidos
|
|
44
|
+
const mergedParams = Object.assign(Object.assign({}, defaultParams), params);
|
|
45
|
+
// Fazer a requisição
|
|
46
|
+
const endpoint = `/search/blended?${createQueryString(mergedParams)}`;
|
|
47
|
+
const response = yield fetchData(endpoint);
|
|
48
|
+
// Processar os dados da resposta
|
|
49
|
+
const newElements = [];
|
|
50
|
+
if (response.data && response.data.elements) {
|
|
51
|
+
for (let i = 0; i < response.data.elements.length; i++) {
|
|
52
|
+
if (response.data.elements[i].elements) {
|
|
53
|
+
newElements.push(...response.data.elements[i].elements);
|
|
54
|
+
}
|
|
55
|
+
// Comentário: não tenho certeza do que extendedElements geralmente se refere
|
|
56
|
+
// - busca por palavra-chave retorna um único trabalho?
|
|
57
|
+
// if (response.data.elements[i].extendedElements) {
|
|
58
|
+
// newElements.push(...response.data.elements[i].extendedElements);
|
|
59
|
+
// }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Adicionar novos elementos aos resultados
|
|
63
|
+
const updatedResults = [...results, ...newElements];
|
|
64
|
+
// Sempre cortar os resultados, não importa o que a requisição retorna
|
|
65
|
+
const trimmedResults = limit
|
|
66
|
+
? updatedResults.slice(0, limit)
|
|
67
|
+
: updatedResults;
|
|
68
|
+
// Caso base da recursão
|
|
69
|
+
const shouldStop = (limit !== undefined &&
|
|
70
|
+
(trimmedResults.length >= limit || // se nossos resultados excedem o limite definido
|
|
71
|
+
trimmedResults.length / count >= MAX_REPEATED_REQUESTS)) ||
|
|
72
|
+
newElements.length === 0;
|
|
73
|
+
if (shouldStop) {
|
|
74
|
+
return trimmedResults;
|
|
75
|
+
}
|
|
76
|
+
// Chamada recursiva
|
|
77
|
+
return (0, exports.search)(params, {
|
|
78
|
+
limit,
|
|
79
|
+
results: trimmedResults,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
exports.search = search;
|
|
83
|
+
// Função auxiliar para busca simples (não recursiva)
|
|
84
|
+
// export const searchSingle = async (
|
|
85
|
+
// client: ClientState,
|
|
86
|
+
// params: SearchParams
|
|
87
|
+
// ): Promise<SearchElement[]> => {
|
|
88
|
+
// return search(client, params, { limit: MAX_SEARCH_COUNT });
|
|
89
|
+
// };
|
|
90
|
+
// // Função para busca com limite específico
|
|
91
|
+
// export const searchWithLimit = async (
|
|
92
|
+
// client: ClientState,
|
|
93
|
+
// params: SearchParams,
|
|
94
|
+
// limit: number
|
|
95
|
+
// ): Promise<SearchElement[]> => {
|
|
96
|
+
// return search(client, params, { limit });
|
|
97
|
+
// };
|
|
98
|
+
// // Função para busca de pessoas
|
|
99
|
+
// export const searchPeople = async (
|
|
100
|
+
// client: ClientState,
|
|
101
|
+
// query: string,
|
|
102
|
+
// limit?: number
|
|
103
|
+
// ): Promise<SearchElement[]> => {
|
|
104
|
+
// const params: SearchParams = {
|
|
105
|
+
// keywords: query,
|
|
106
|
+
// filters: "List(resultType->PEOPLE)",
|
|
107
|
+
// };
|
|
108
|
+
// return search(client, params, { limit });
|
|
109
|
+
// };
|
|
110
|
+
// // Função para busca de empresas
|
|
111
|
+
// export const searchCompanies = async (
|
|
112
|
+
// client: ClientState,
|
|
113
|
+
// query: string,
|
|
114
|
+
// limit?: number
|
|
115
|
+
// ): Promise<SearchElement[]> => {
|
|
116
|
+
// const params: SearchParams = {
|
|
117
|
+
// keywords: query,
|
|
118
|
+
// filters: "List(resultType->COMPANIES)",
|
|
119
|
+
// };
|
|
120
|
+
// return search(client, params, { limit });
|
|
121
|
+
// };
|
|
122
|
+
// // Função para busca de empregos
|
|
123
|
+
// export const searchJobs = async (
|
|
124
|
+
// client: ClientState,
|
|
125
|
+
// query: string,
|
|
126
|
+
// limit?: number
|
|
127
|
+
// ): Promise<SearchElement[]> => {
|
|
128
|
+
// const params: SearchParams = {
|
|
129
|
+
// keywords: query,
|
|
130
|
+
// filters: "List(resultType->JOBS)",
|
|
131
|
+
// };
|
|
132
|
+
// return search(client, params, { limit });
|
|
133
|
+
// };
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function filterKeys(obj: any, keysToKeep: string[]): any;
|
|
2
|
+
export declare function filterOutKeys(obj: any, keysToIgnore: string[]): any;
|
|
3
|
+
export declare function getNestedValue(obj: any, path: string): any;
|
|
4
|
+
export declare function extractFields(data: any[], fieldsMap: Record<string, string>): any[];
|
|
5
|
+
export declare function debugObjectStructure(obj: any, maxDepth?: number, currentDepth?: number): void;
|
|
6
|
+
export declare function resolveReferences(data: any, included: any[]): any;
|
|
7
|
+
export declare function extractDataWithReferences(elements: string[], included: any[], fieldsMap?: Record<string, string>): any[];
|
|
8
|
+
export declare function debugResolvedStructure(elements: string[], included: any[], maxDepth?: number): void;
|
|
9
|
+
export declare function extractFieldsFromIncluded(included: any[], fields: string[]): Record<string, any>[];
|
|
10
|
+
export declare function mergeExtraFields(mainData: any[], extraData: Record<string, any>[], matchKey?: string): any[];
|