linkedin-api-voyager 1.3.1 → 1.3.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/README.md +226 -137
- package/lib/browser.d.ts +9 -0
- package/lib/browser.js +48 -0
- package/lib/config.d.ts +3 -16
- package/lib/config.js +12 -70
- package/package.json +1 -3
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# LinkedIn API Voyager
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Biblioteca TypeScript para interagir com endpoints internos do LinkedIn (Voyager). Esta não é uma API oficial.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Instalação
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install linkedin-api-voyager
|
|
@@ -10,188 +10,277 @@ npm install linkedin-api-voyager
|
|
|
10
10
|
yarn add linkedin-api-voyager
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Configuração (Obrigatório)
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
A biblioteca funciona tanto no **backend (Node.js)** quanto no **frontend**. Você precisa inicializar o cliente com seus cookies uma única vez antes de fazer qualquer requisição.
|
|
16
16
|
|
|
17
|
-
###
|
|
17
|
+
### 1. Inicialize o Client
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
No ponto de entrada da sua aplicação (ex: `index.ts`, `app.tsx`, `server.ts`):
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { Client } from "linkedin-api-voyager";
|
|
23
|
+
|
|
24
|
+
// Configure suas credenciais uma única vez
|
|
25
|
+
Client({
|
|
26
|
+
JSESSIONID: process.env.LINKEDIN_JSESSIONID, // ex: "ajax:123456789" (apenas os números se preferir, a lib trata)
|
|
27
|
+
li_at: process.env.LINKEDIN_LI_AT, // ex: "AQEDAR..."
|
|
28
|
+
});
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
### 2. Onde pegar `li_at` e `JSESSIONID`
|
|
32
|
+
|
|
33
|
+
1. Faça login no LinkedIn pelo navegador.
|
|
34
|
+
2. Abra o DevTools do navegador.
|
|
35
|
+
3. Vá em:
|
|
36
|
+
- Chrome/Edge: `Application` -> `Storage` -> `Cookies` -> `https://www.linkedin.com`
|
|
37
|
+
- Firefox: `Storage` -> `Cookies` -> `https://www.linkedin.com`
|
|
38
|
+
4. Copie os valores:
|
|
39
|
+
- `li_at`: valor completo.
|
|
40
|
+
- `JSESSIONID`: valor completo (ex: `"ajax:123456789"`).
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
> **Nota:** Nunca comite suas credenciais reais no código. Use variáveis de ambiente (`.env`).
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
## Exemplos de Uso
|
|
45
|
+
|
|
46
|
+
Após inicializar o `Client`, você pode importar e usar qualquer função diretamente:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
32
49
|
import {
|
|
33
|
-
|
|
50
|
+
getUserMiniProfile,
|
|
34
51
|
getProfissionalExperiences,
|
|
35
52
|
getCompany,
|
|
36
|
-
|
|
53
|
+
searchPeople,
|
|
54
|
+
getCommentsByPostUrl,
|
|
37
55
|
} from "linkedin-api-voyager";
|
|
56
|
+
|
|
57
|
+
// Exemplo: Buscar perfil
|
|
58
|
+
const profile = await getUserMiniProfile("wesbush");
|
|
59
|
+
console.log(profile);
|
|
60
|
+
|
|
61
|
+
// Exemplo: Buscar experiências
|
|
62
|
+
const experiences = await getProfissionalExperiences("wesbush");
|
|
63
|
+
|
|
64
|
+
// Exemplo: Buscar empresa
|
|
65
|
+
const company = await getCompany("microsoft");
|
|
66
|
+
|
|
67
|
+
// Exemplo: Pesquisar pessoas
|
|
68
|
+
const people = await searchPeople("software engineer");
|
|
69
|
+
|
|
70
|
+
// Exemplo: Buscar comentários
|
|
71
|
+
const comments = await getCommentsByPostUrl(
|
|
72
|
+
"https://www.linkedin.com/feed/update/urn:li:activity-1234567890/",
|
|
73
|
+
);
|
|
38
74
|
```
|
|
39
75
|
|
|
40
|
-
|
|
76
|
+
## API
|
|
77
|
+
|
|
78
|
+
### `src/config.ts`
|
|
79
|
+
|
|
80
|
+
- `Client(config: { JSESSIONID: string; li_at: string })`: Configura a instância global do axios. Deve ser chamado antes de qualquer outra função.
|
|
81
|
+
- `API_BASE_URL`: `https://www.linkedin.com/voyager/api`
|
|
41
82
|
|
|
42
|
-
|
|
43
|
-
// Obter perfil completo
|
|
44
|
-
const profile = await getProfile("username-do-linkedin");
|
|
83
|
+
### Módulos Disponíveis
|
|
45
84
|
|
|
46
|
-
|
|
47
|
-
|
|
85
|
+
A biblioteca exporta funções dos seguintes módulos:
|
|
86
|
+
- `user`: Perfis e dados de usuário.
|
|
87
|
+
- `company`: Dados de empresas.
|
|
88
|
+
- `posts`: Interações com posts e comentários.
|
|
89
|
+
- `search`: Busca de pessoas e empresas.
|
|
90
|
+
- `utils`: Utilitários gerais.
|
|
48
91
|
```
|
|
49
92
|
|
|
50
|
-
###
|
|
93
|
+
### `src/user.ts`
|
|
94
|
+
|
|
95
|
+
Tipos exportados:
|
|
96
|
+
|
|
97
|
+
- `MiniUserProfileLinkedin`
|
|
98
|
+
|
|
99
|
+
Funções exportadas:
|
|
100
|
+
|
|
101
|
+
- `getUserMiniProfile(identifier: string): Promise<MiniUserProfileLinkedin>`
|
|
102
|
+
- Busca dados básicos do perfil (nome, headline, imagens) e também o `about`.
|
|
103
|
+
- `identifier` é o `publicIdentifier` (parte final da URL `linkedin.com/in/<identifier>`).
|
|
104
|
+
|
|
105
|
+
- `extractProfileIdLinkedin(profileUrl: string): Promise<string | null>`
|
|
106
|
+
- Extrai o `publicIdentifier` de uma URL `linkedin.com/in/...`.
|
|
107
|
+
- Se você passar apenas o identificador, ele tenta usar diretamente.
|
|
108
|
+
- Retorna o ID numérico interno (sem o prefixo `urn:li:fsd_profile:`) quando encontra.
|
|
109
|
+
|
|
110
|
+
- `getProfileSectionAbout(identifier: string): Promise<string | null>`
|
|
111
|
+
- Retorna o texto de “Sobre” (about) do perfil.
|
|
112
|
+
|
|
113
|
+
- `getProfissionalExperiences(identifier: string): Promise<Array<any>>`
|
|
114
|
+
- Retorna a lista de experiências profissionais.
|
|
115
|
+
- Para cada experiência, tenta enriquecer com dados de empresa via `getCompany`.
|
|
116
|
+
|
|
117
|
+
- `getContactInfo(identifier: string): Promise<{ ... }>`
|
|
118
|
+
- Retorna informações de contato quando disponíveis (email, telefones, sites etc.).
|
|
119
|
+
|
|
120
|
+
- `getLinkedinSkills(identifier: string): Promise<Array<string | null>>`
|
|
121
|
+
- Retorna as skills (habilidades) listadas no perfil.
|
|
122
|
+
|
|
123
|
+
- `getLinkedinEducation(identifier: string): Promise<Array<any>>`
|
|
124
|
+
- Retorna a educação (escola, degree, datas, skills relacionadas quando houver).
|
|
125
|
+
|
|
126
|
+
- `getLinkedinCertifications(identifier: string): Promise<Array<any>>`
|
|
127
|
+
- Retorna certificações.
|
|
128
|
+
|
|
129
|
+
Exemplo:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import {
|
|
133
|
+
getUserMiniProfile,
|
|
134
|
+
getProfileSectionAbout,
|
|
135
|
+
getProfissionalExperiences,
|
|
136
|
+
getContactInfo,
|
|
137
|
+
getLinkedinSkills,
|
|
138
|
+
getLinkedinEducation,
|
|
139
|
+
getLinkedinCertifications,
|
|
140
|
+
} from "linkedin-api-voyager";
|
|
141
|
+
|
|
142
|
+
const identifier = "wesbush";
|
|
51
143
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
144
|
+
const mini = await getUserMiniProfile(identifier);
|
|
145
|
+
const about = await getProfileSectionAbout(identifier);
|
|
146
|
+
const experiences = await getProfissionalExperiences(identifier);
|
|
147
|
+
const contact = await getContactInfo(identifier);
|
|
148
|
+
const skills = await getLinkedinSkills(identifier);
|
|
149
|
+
const education = await getLinkedinEducation(identifier);
|
|
150
|
+
const certifications = await getLinkedinCertifications(identifier);
|
|
55
151
|
```
|
|
56
152
|
|
|
57
|
-
###
|
|
153
|
+
### `src/company.ts`
|
|
58
154
|
|
|
59
|
-
|
|
60
|
-
// Busca geral
|
|
61
|
-
const results = await search(
|
|
62
|
-
{
|
|
63
|
-
keywords: "desenvolvedor javascript",
|
|
64
|
-
filters: "List(resultType->PEOPLE)",
|
|
65
|
-
},
|
|
66
|
-
{ limit: 50 }
|
|
67
|
-
);
|
|
155
|
+
Funções exportadas:
|
|
68
156
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
157
|
+
- `getCompany(identifier: string): Promise<any>`
|
|
158
|
+
- Busca dados de uma empresa pelo `universalName` (slug da página).
|
|
159
|
+
- Exemplo de slug: `https://www.linkedin.com/company/microsoft/` -> `microsoft`.
|
|
160
|
+
|
|
161
|
+
Exemplo:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { getCompany } from "linkedin-api-voyager";
|
|
165
|
+
|
|
166
|
+
const company = await getCompany("microsoft");
|
|
77
167
|
```
|
|
78
168
|
|
|
79
|
-
###
|
|
169
|
+
### `src/posts.ts`
|
|
170
|
+
|
|
171
|
+
Funções exportadas:
|
|
172
|
+
|
|
173
|
+
- `parseResponsePostLinkedin(response: any, key: string, accumulatedData: any): any`
|
|
174
|
+
- Helper para selecionar itens do `included` a partir de `*elements`.
|
|
175
|
+
|
|
176
|
+
- `getCommentsByPostUrl(url: string, start = 0, limit = 50, accumulatedComments: unknown[] = []): Promise<unknown[]>`
|
|
177
|
+
- Busca comentários de um post (paginando recursivamente até acabar).
|
|
178
|
+
|
|
179
|
+
- `getPosts(): Promise<unknown[]>`
|
|
180
|
+
- Atualmente retorna `[]` (placeholder).
|
|
181
|
+
|
|
182
|
+
- `getPostLinkedin(url: string, commentsCount = 10, likesCount = 10): Promise<any>`
|
|
183
|
+
- Busca um post pelo slug da URL e retorna os dados do post e do autor.
|
|
184
|
+
|
|
185
|
+
- `getUserPosts({ identifier, start = 0, count = 50, accumulatedPosts = [] }): Promise<any>`
|
|
186
|
+
- Busca posts do usuário por `identifier` (publicIdentifier).
|
|
187
|
+
|
|
188
|
+
- `helperGetPosts(response: any, key: string, accumulatedPosts?: any, addFields?: Record<string, string>): any`
|
|
189
|
+
- Helper para extrair posts e contagens (likes, comentários, shares).
|
|
190
|
+
|
|
191
|
+
- `helperGetImageUrl(item: any): string`
|
|
192
|
+
- Helper para montar a URL de imagem, priorizando o maior artifact.
|
|
193
|
+
|
|
194
|
+
Exemplo (comentários):
|
|
80
195
|
|
|
81
|
-
```
|
|
196
|
+
```ts
|
|
82
197
|
import { getCommentsByPostUrl } from "linkedin-api-voyager";
|
|
83
198
|
|
|
84
|
-
// Obter todos os comentários de um post
|
|
85
199
|
const comments = await getCommentsByPostUrl(
|
|
86
200
|
"https://www.linkedin.com/feed/update/urn:li:activity-1234567890/",
|
|
87
|
-
0, // início
|
|
88
|
-
50 // limite por página
|
|
89
201
|
);
|
|
90
202
|
```
|
|
91
203
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
### Perfil
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
{
|
|
98
|
-
publicIdentifier: string;
|
|
99
|
-
firstName: string;
|
|
100
|
-
lastName: string;
|
|
101
|
-
fullName: string;
|
|
102
|
-
profilePicture: string;
|
|
103
|
-
backgroundPicture: string;
|
|
104
|
-
location: {
|
|
105
|
-
country: string;
|
|
106
|
-
city: string;
|
|
107
|
-
}
|
|
108
|
-
industry: string;
|
|
109
|
-
headline: string;
|
|
110
|
-
summary: string;
|
|
111
|
-
// ... outros campos
|
|
112
|
-
}
|
|
113
|
-
```
|
|
204
|
+
### `src/search.ts`
|
|
114
205
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
[
|
|
119
|
-
{
|
|
120
|
-
id: string;
|
|
121
|
-
title: string;
|
|
122
|
-
companyName: string;
|
|
123
|
-
companyUrn: string;
|
|
124
|
-
universalName: string; // Nome universal da empresa
|
|
125
|
-
description: string;
|
|
126
|
-
location: string;
|
|
127
|
-
startDate: { year: number; month: number };
|
|
128
|
-
endDate: { year: number; month: number } | null; // null = ativo
|
|
129
|
-
// ... outros campos
|
|
130
|
-
}
|
|
131
|
-
]
|
|
132
|
-
```
|
|
206
|
+
Constantes internas:
|
|
133
207
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{
|
|
138
|
-
id: string;
|
|
139
|
-
name: string;
|
|
140
|
-
description: string;
|
|
141
|
-
username: string;
|
|
142
|
-
companyPageUrl: string;
|
|
143
|
-
staffCount: number;
|
|
144
|
-
url: string;
|
|
145
|
-
location: string;
|
|
146
|
-
followerCount: number;
|
|
147
|
-
logo: object;
|
|
148
|
-
// ... outros campos
|
|
149
|
-
}
|
|
150
|
-
```
|
|
208
|
+
- `MAX_SEARCH_COUNT = 25` (limite máximo por chamada na busca geral)
|
|
209
|
+
|
|
210
|
+
Funções exportadas:
|
|
151
211
|
|
|
152
|
-
|
|
212
|
+
- `search(params: ISearchParams): Promise<SearchResponse>`
|
|
213
|
+
- Busca geral usando `query` e/ou `filters` (formato Voyager).
|
|
214
|
+
- Aceita paginação via `offset`.
|
|
153
215
|
|
|
154
|
-
|
|
216
|
+
- `searchPeople(queryOrParams: string | ISearchPeopleParams): Promise<ISearchPeopleResponse>`
|
|
217
|
+
- Busca pessoas com helpers para montar filtros (networkDepth, regiões, empresas etc.).
|
|
155
218
|
|
|
156
|
-
|
|
157
|
-
|
|
219
|
+
Exemplo:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import { search, searchPeople } from "linkedin-api-voyager";
|
|
223
|
+
|
|
224
|
+
const res = await search({ query: "react developer" });
|
|
225
|
+
const people = await searchPeople({
|
|
226
|
+
query: "engenheiro de software",
|
|
227
|
+
regions: ["br:0"],
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `src/utils.ts`
|
|
232
|
+
|
|
233
|
+
Funções exportadas (helpers usados em parsing e normalização):
|
|
234
|
+
|
|
235
|
+
- `filterKeys(obj: any, keysToKeep: string[]): any`
|
|
236
|
+
- `filterOutKeys(obj: any, keysToIgnore: string[]): any`
|
|
237
|
+
- `getNestedValue(obj: any, path: string): any`
|
|
238
|
+
- `extractFields(data: any[], fieldsMap: Record<string, string>): any[]`
|
|
239
|
+
- `debugObjectStructure(obj: any, maxDepth = 3, currentDepth = 0): void`
|
|
240
|
+
- `resolveReferences(data: any, included: any[]): any`
|
|
241
|
+
- `extractDataWithReferences(elements: string[], included: any[], fieldsMap?: Record<string, string>): any[]`
|
|
242
|
+
- `debugResolvedStructure(elements: string[], included: any[], maxDepth = 2): void`
|
|
243
|
+
- `extractFieldsFromIncluded(included: any[], fields: string[]): Array<Record<string, any>>`
|
|
244
|
+
- `mergeExtraFields(mainData: any[], extraData: Array<Record<string, any>>, matchKey = "companyUrn"): any[]`
|
|
245
|
+
- `getDataIncludedForEntity(jsonData: Record<string, any>, entityUrn: string): any`
|
|
246
|
+
- `extractExperiences(jsonData: Record<string, any>): Array<{ role: string | null; idCompany: string | null; company: string | null; ... }>`
|
|
247
|
+
- `assert(value: unknown, message?: string | Error): asserts value`
|
|
248
|
+
- `getIdFromUrn(urn?: string): string | undefined`
|
|
249
|
+
- `getUrnFromRawUpdate(update?: string): string | undefined`
|
|
250
|
+
- `isLinkedInUrn(urn?: string): boolean`
|
|
251
|
+
- `parseExperienceItem(item: any, opts: { isGroupItem?: boolean; included: any[] }): ExperienceItem`
|
|
252
|
+
- `getGroupedItemId(item: any): string | undefined`
|
|
253
|
+
- `omit(inputObj: object, ...keys: string[]): object`
|
|
254
|
+
- `resolveImageUrl(vectorImage?: VectorImage): string | undefined`
|
|
255
|
+
- `resolveLinkedVectorImageUrl(linkedVectorImage?: LinkedVectorImage): string | undefined`
|
|
256
|
+
- `stringifyLinkedInDate(date?: LIDate): string | undefined`
|
|
257
|
+
- `normalizeRawOrganization(o?: RawOrganization): Organization`
|
|
258
|
+
|
|
259
|
+
Exemplo (mapear campos com path aninhado):
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { extractFields } from "linkedin-api-voyager";
|
|
158
263
|
|
|
159
|
-
// Mapear campos específicos
|
|
160
264
|
const fieldsMap = {
|
|
161
265
|
nome: "firstName",
|
|
162
|
-
|
|
163
|
-
|
|
266
|
+
headline: "headline",
|
|
267
|
+
foto: "profilePicture.displayImageReferenceResolutionResult.vectorImage.rootUrl",
|
|
164
268
|
};
|
|
165
269
|
|
|
166
|
-
const
|
|
270
|
+
const mapped = extractFields([someObject], fieldsMap);
|
|
167
271
|
```
|
|
168
272
|
|
|
169
|
-
###
|
|
170
|
-
|
|
171
|
-
A biblioteca resolve automaticamente referências URN aninhadas, permitindo acesso direto a dados relacionados sem necessidade de mapeamento manual.
|
|
273
|
+
### `src/types.ts`
|
|
172
274
|
|
|
173
|
-
|
|
275
|
+
Este arquivo exporta tipos e interfaces TypeScript usados pela biblioteca (por exemplo: `ISearchParams`, `ISearchPeopleParams`, `SearchResponse`, `Organization`, `ExperienceItem`).
|
|
174
276
|
|
|
175
|
-
|
|
176
|
-
- Requer cookies válidos de uma sessão autenticada
|
|
177
|
-
- Respeite os termos de uso do LinkedIn
|
|
178
|
-
- Use com moderação para evitar bloqueios
|
|
179
|
-
- Não é uma API oficial do LinkedIn
|
|
277
|
+
## Limitações e considerações
|
|
180
278
|
|
|
181
|
-
|
|
279
|
+
- Usa endpoints internos do LinkedIn (Voyager), que podem mudar sem aviso.
|
|
280
|
+
- Requer cookies válidos de uma sessão autenticada.
|
|
281
|
+
- Use com moderação para reduzir risco de bloqueio.
|
|
282
|
+
- Respeite os termos de uso do LinkedIn.
|
|
182
283
|
|
|
183
|
-
|
|
184
|
-
- Não compartilhe o arquivo `linkedin_cookies.json`
|
|
185
|
-
- O arquivo já está incluído no `.gitignore`
|
|
186
|
-
|
|
187
|
-
## 📝 Licença
|
|
284
|
+
## Licença
|
|
188
285
|
|
|
189
286
|
MIT
|
|
190
|
-
|
|
191
|
-
## 🤝 Contribuição
|
|
192
|
-
|
|
193
|
-
Contribuições são bem-vindas! Por favor, abra uma issue ou pull request.
|
|
194
|
-
|
|
195
|
-
## 📞 Suporte
|
|
196
|
-
|
|
197
|
-
Para dúvidas ou problemas, abra uma issue no repositório.
|
package/lib/browser.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./utils";
|
|
3
|
+
export declare const COOKIE_FILE_PATH = "linkedin_cookies.json";
|
|
4
|
+
export declare const API_BASE_URL = "https://www.linkedin.com/voyager/api";
|
|
5
|
+
export declare const AUTH_BASE_URL = "https://www.linkedin.com";
|
|
6
|
+
export declare const saveCookies: () => Promise<never>;
|
|
7
|
+
export declare const loadCookies: () => Promise<never>;
|
|
8
|
+
export declare const Client: () => Promise<never>;
|
|
9
|
+
export declare const fetchData: () => Promise<never>;
|
package/lib/browser.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
17
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
18
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
19
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
20
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
21
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
22
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.fetchData = exports.Client = exports.loadCookies = exports.saveCookies = exports.AUTH_BASE_URL = exports.API_BASE_URL = exports.COOKIE_FILE_PATH = void 0;
|
|
27
|
+
const ERROR_BROWSER_ONLY = "linkedin-api-voyager: este pacote não roda no browser. Use um Route Handler (Next.js) no servidor.";
|
|
28
|
+
__exportStar(require("./types"), exports);
|
|
29
|
+
__exportStar(require("./utils"), exports);
|
|
30
|
+
exports.COOKIE_FILE_PATH = "linkedin_cookies.json";
|
|
31
|
+
exports.API_BASE_URL = "https://www.linkedin.com/voyager/api";
|
|
32
|
+
exports.AUTH_BASE_URL = "https://www.linkedin.com";
|
|
33
|
+
const saveCookies = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
|
+
throw new Error(ERROR_BROWSER_ONLY);
|
|
35
|
+
});
|
|
36
|
+
exports.saveCookies = saveCookies;
|
|
37
|
+
const loadCookies = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
throw new Error(ERROR_BROWSER_ONLY);
|
|
39
|
+
});
|
|
40
|
+
exports.loadCookies = loadCookies;
|
|
41
|
+
const Client = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
|
+
throw new Error(ERROR_BROWSER_ONLY);
|
|
43
|
+
});
|
|
44
|
+
exports.Client = Client;
|
|
45
|
+
const fetchData = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
46
|
+
throw new Error(ERROR_BROWSER_ONLY);
|
|
47
|
+
});
|
|
48
|
+
exports.fetchData = fetchData;
|
package/lib/config.d.ts
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
2
|
export declare const API_BASE_URL = "https://www.linkedin.com/voyager/api";
|
|
3
|
-
export declare const
|
|
4
|
-
interface LinkedInCookies {
|
|
3
|
+
export declare const Client: (providedCookies: {
|
|
5
4
|
JSESSIONID: string;
|
|
6
5
|
li_at: string;
|
|
7
|
-
|
|
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;
|
|
6
|
+
}) => AxiosInstance;
|
|
19
7
|
export declare const fetchData: (endpoint: string) => Promise<any>;
|
|
20
|
-
export {};
|
package/lib/config.js
CHANGED
|
@@ -9,86 +9,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.fetchData = exports.Client = exports.
|
|
13
|
-
const fs = require("fs-extra");
|
|
12
|
+
exports.fetchData = exports.Client = exports.API_BASE_URL = void 0;
|
|
14
13
|
const axios_1 = require("axios");
|
|
15
|
-
exports.COOKIE_FILE_PATH = "linkedin_cookies.json";
|
|
16
14
|
exports.API_BASE_URL = "https://www.linkedin.com/voyager/api";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
exports.saveCookies = saveCookies;
|
|
34
|
-
// Função para carregar cookies do arquivo JSON
|
|
35
|
-
const loadCookies = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
-
try {
|
|
37
|
-
const exists = yield fs.pathExists(exports.COOKIE_FILE_PATH);
|
|
38
|
-
if (!exists) {
|
|
39
|
-
throw new Error("Arquivo de cookies não encontrado");
|
|
40
|
-
}
|
|
41
|
-
const cookies = yield fs.readJson(exports.COOKIE_FILE_PATH);
|
|
42
|
-
// Verificar se os cookies têm a estrutura esperada
|
|
43
|
-
if (!cookies.JSESSIONID || !cookies.li_at) {
|
|
44
|
-
throw new Error("Cookies inválidos encontrados no arquivo");
|
|
45
|
-
}
|
|
46
|
-
return cookies;
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
exports.loadCookies = loadCookies;
|
|
53
|
-
// Função para criar cliente com cookies automáticos
|
|
54
|
-
const Client = (providedCookies) => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
|
-
let cookiesToUse;
|
|
56
|
-
const savedCookies = yield (0, exports.loadCookies)();
|
|
57
|
-
if (savedCookies) {
|
|
58
|
-
cookiesToUse = {
|
|
59
|
-
JSESSIONID: savedCookies.JSESSIONID,
|
|
60
|
-
li_at: savedCookies.li_at,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
if (providedCookies) {
|
|
65
|
-
yield (0, exports.saveCookies)(providedCookies.JSESSIONID, providedCookies.li_at);
|
|
66
|
-
cookiesToUse = providedCookies;
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
throw new Error("Nenhum cookie válido fornecido");
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return api({
|
|
73
|
-
JSESSIONID: parseInt(cookiesToUse.JSESSIONID),
|
|
74
|
-
li_at: cookiesToUse.li_at,
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
exports.Client = Client;
|
|
78
|
-
const api = ({ JSESSIONID, li_at }) => {
|
|
79
|
-
return axios_1.default.create({
|
|
15
|
+
let apiInstance = null;
|
|
16
|
+
const Client = (providedCookies) => {
|
|
17
|
+
apiInstance = axios_1.default.create({
|
|
80
18
|
baseURL: exports.API_BASE_URL,
|
|
81
19
|
headers: {
|
|
82
20
|
"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",
|
|
83
21
|
accept: "application/vnd.linkedin.normalized+json+2.1",
|
|
84
|
-
cookie: `li_at=${li_at}; JSESSIONID="ajax:${JSESSIONID}"`,
|
|
85
|
-
"csrf-token": `ajax:${JSESSIONID}`,
|
|
22
|
+
cookie: `li_at=${providedCookies.li_at}; JSESSIONID="ajax:${providedCookies.JSESSIONID}"`,
|
|
23
|
+
"csrf-token": `ajax:${providedCookies.JSESSIONID}`,
|
|
86
24
|
},
|
|
87
25
|
});
|
|
26
|
+
return apiInstance;
|
|
88
27
|
};
|
|
28
|
+
exports.Client = Client;
|
|
89
29
|
const fetchData = (endpoint) => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
-
|
|
91
|
-
|
|
30
|
+
if (!apiInstance) {
|
|
31
|
+
throw new Error("Client not initialized. Please call Client({ JSESSIONID, li_at }) first.");
|
|
32
|
+
}
|
|
33
|
+
const response = yield apiInstance.get(endpoint);
|
|
92
34
|
return response.data;
|
|
93
35
|
});
|
|
94
36
|
exports.fetchData = fetchData;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linkedin-api-voyager",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "Uma biblioteca TypeScript para interagir com a API interna do LinkedIn (Voyager)",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"prepublishOnly": "npm run build"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@types/fs-extra": "^11.0.4",
|
|
19
18
|
"@types/node": "^22.13.1",
|
|
20
19
|
"nodemon": "^3.1.9",
|
|
21
20
|
"ts-node": "^10.9.2",
|
|
@@ -23,7 +22,6 @@
|
|
|
23
22
|
},
|
|
24
23
|
"dependencies": {
|
|
25
24
|
"axios": "^1.11.0",
|
|
26
|
-
"fs-extra": "^11.3.1",
|
|
27
25
|
"path": "^0.12.7"
|
|
28
26
|
}
|
|
29
27
|
}
|