From 97ec9c9f87e4605cd4dfed3128eea8fcec812723 Mon Sep 17 00:00:00 2001 From: ION606 Date: Fri, 31 May 2024 15:49:51 -0400 Subject: [PATCH] added connections --- .gitignore | 1 + .readthedocs.yaml | 20 -------------------- README.md | 2 +- classes/API.js | 47 ++++++++++++++++++++++++++++++++++++++++++++++ classes/Profile.js | 17 +++++++++++++++++ package.json | 2 +- 6 files changed, 67 insertions(+), 22 deletions(-) delete mode 100644 .readthedocs.yaml diff --git a/.gitignore b/.gitignore index a022392..515af46 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,4 @@ dist temp.* config.json cookie.txt +temp/ \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 42f088f..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -build: - os: ubuntu-lts-latest - tools: - nodejs: "20" - -# Build documentation in the "docs/" directory with Sphinx -sphinx: - configuration: docs/conf.py - builder: "dirhtml" - - -# Optionally build your docs in additional formats such as PDF and ePub -formats: - - pdf - - epub \ No newline at end of file diff --git a/README.md b/README.md index f9b40b8..b23f935 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This package is not in any way affiliated with LinkedIn. In fact, your account m You can use your LinkedIn credentials in code, making this super simple to use! -*This project was HEAVILY inspired by Tomquirk's LinkedIn-API PyPy package, which you can find at https://github.com/tomquirk/linkedin-api* +*This project was HEAVILY inspired by Tomquirk's LinkedIn-API PyPi package, which you can find at https://github.com/tomquirk/linkedin-api* ## Installation `npm i linkedin-api-js` diff --git a/classes/API.js b/classes/API.js index 49cb75c..e4cb588 100644 --- a/classes/API.js +++ b/classes/API.js @@ -226,6 +226,53 @@ export default class linkedInAPIClass { } + /** + * @param {number[]} conDeg + * @param {number} [limit=1000] + */ + async getConnections(entityNameJoined, entityUrn, conDeg, limit = 1000, start = 0) { + const empAll = []; + + if (this.logAll) console.log(`scanning for ${limit} connections for user "${entityNameJoined}"`); + + const lb = (this.logAll) ? new LoadingBar(Math.round(limit / 50)) : null; + + for (let i = start; empAll.length < limit; i += 50) { + let urlExt = `variables=(start:${i},origin:MEMBER_PROFILE_CANNED_SEARCH,memberIdentity:${entityNameJoined},query:(flagshipSearchIntent:SEARCH_SRP,queryParameters:List((key:connectionOf,value:List(${entityUrn})),(key:network,value:List(${numToConDegs(conDeg)})),(key:resultType,value:List(PEOPLE))),includeFiltersInResponse:false))`; + const r = await this._makeReq(urlExt); + + if (!r?.included && r?.data?.errors) { + console.error(JSON.stringify(r.data.errors)) + throw "ERROR!"; + } + else if (!r?.data.included) break; + + empAll.push(r); + + if (this.logAll) lb.increment(Math.ceil(r.included.length / 50)); + await this.evade(); + } + + return empAll.flat(); + } + + + async sendConnectionRequest(memberURN, customMessage = undefined) { + await this.evade(); + + const rPath = 'https://www.linkedin.com/voyager/api/voyagerRelationshipsDashMemberRelationships?action=verifyQuotaAndCreateV2&decorationId=com.linkedin.voyager.dash.deco.relationships.InvitationCreationResultWithInvitee-2'; + const body = { + "invitee": + { "inviteeUnion": { "memberProfile": `urn:li:fsd_profile:${memberURN}` } }, + customMessage + } + const headers = this.headers; + headers['x-li-pem-metadata'] = 'Voyager - Invitations - Actions=invite-send'; + + const r = await axios.post(rPath, body, { headers }); // await axios.get(rPath, body, { headers }).catch(_ => null).then(_ => null).finally(() => axios.post(rPath, body, { headers })); + return r.data; + } + /** * @returns {Promise} * @param {String} keyword the user to search for diff --git a/classes/Profile.js b/classes/Profile.js index 8e4d417..72a2815 100644 --- a/classes/Profile.js +++ b/classes/Profile.js @@ -1,4 +1,5 @@ import linkedInAPIClass from "../index.js"; +import { numToConDegs } from "./API.js"; export class LinkedInProfile { /** @type {linkedInAPIClass} */ @@ -29,12 +30,28 @@ export class LinkedInProfile { return ''; } + /** + * @param {number[]} conDeg + * @param {number} [limit=1000] + */ + async getConnections (conDeg, limit = 1000) { + if (!this.isConnected) throw `USER ${this.name} IS OUT OF NETWORK!`; + return await this.#APIRef.getConnections(this.entityNameJoined, this.entityUrn, conDeg, limit); + } + + /** + * will send a friend request to this person + * @param {string} message + */ + makeConnection = (message) => this.#APIRef.sendConnectionRequest(this.entityUrn, message); + constructor(jsonData, apiRef) { this.#APIRef = apiRef; this.name = jsonData.title ? jsonData.title.text : ''; this.entityNameJoined = jsonData.navigationUrl?.split("/in/")[1]?.split("?")[0]; this.profileUrl = jsonData.navigationUrl || ''; this.trackingUrn = jsonData.trackingUrn || ''; + this.isConnected = (jsonData.entityCustomTrackingInfo?.memberDistance !== 'OUT_OF_NETWORK'); const match = jsonData.entityUrn?.match(/urn:li:fsd_profile:(.*?),/); this.entityUrn = match ? match[1] : ''; diff --git a/package.json b/package.json index d88e2c2..daa5f0e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "type": "module", "name": "linkedin-api-js", - "version": "1.0.0-21", + "version": "1.0.0-22", "main": "index.js", "repository": { "type": "git",