coffeeinabit 0.0.1

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.
@@ -0,0 +1,423 @@
1
+ import axios from 'axios';
2
+
3
+ export async function executeGetNewMessages(page, action, accessToken) {
4
+ let lastChecked = action.parameters?.last_checked;
5
+ if (!lastChecked) {
6
+ lastChecked = 0;
7
+ }
8
+
9
+ let lastCheckedTimestamp = parseInt(lastChecked, 10);
10
+ if (lastCheckedTimestamp < 1e12) {
11
+ lastCheckedTimestamp = lastCheckedTimestamp * 1000;
12
+ }
13
+
14
+ const allMessageEntities = [];
15
+ const processedMessages = new Set();
16
+ const conversationParticipantsMap = new Map();
17
+
18
+ const routeHandler = async (route) => {
19
+ const url = route.request().url();
20
+
21
+ /////
22
+ if (url.includes('voyagerMessagingGraphQL')) {
23
+ const requestBody = route.request().postData();
24
+ if (requestBody) {
25
+ try {
26
+ const parsed = JSON.parse(requestBody);
27
+ console.log(`[GetNewMessages] GraphQL request: ${parsed.queryId || 'unknown'}`);
28
+ } catch (e) {
29
+ }
30
+ }
31
+ }
32
+
33
+ if (url.includes('messengerConversations')) {
34
+ const response = await route.fetch();
35
+
36
+ try {
37
+ const responseBody = await response.text();
38
+ const data = JSON.parse(responseBody);
39
+
40
+ if (data?.data?.messengerConversationsBySyncToken?.elements) {
41
+ const conversations = data.data.messengerConversationsBySyncToken.elements;
42
+ conversations.forEach(conv => {
43
+ const convUrn = conv.backendUrn;
44
+ const participants = conv.conversationParticipants || [];
45
+ const otherPerson = participants.find(p => {
46
+ const member = p.participantType?.member;
47
+ return member && member.distance !== 'SELF';
48
+ });
49
+
50
+ if (otherPerson && otherPerson.hostIdentityUrn) {
51
+ const encodedUrl = otherPerson.hostIdentityUrn.includes('urn:li:fsd_profile:')
52
+ ? otherPerson.hostIdentityUrn.split('urn:li:fsd_profile:')[1]
53
+ : '';
54
+ if (encodedUrl) {
55
+ conversationParticipantsMap.set(convUrn, encodedUrl);
56
+ }
57
+ }
58
+ });
59
+ }
60
+ } catch (error) {
61
+ console.error('[GetNewMessages] Error parsing conversations:', error);
62
+ }
63
+
64
+ try {
65
+ await route.fulfill({ response });
66
+ } catch (fulfillError) {
67
+ }
68
+ return;
69
+ }
70
+
71
+ if (url.includes('messengerMessages')) {
72
+ const response = await route.fetch();
73
+
74
+ try {
75
+ const responseBody = await response.text();
76
+ const data = JSON.parse(responseBody);
77
+
78
+ console.log(`[GetNewMessages] Intercepted messengerMessages response`);
79
+
80
+ if (data?.data?.messengerMessagesBySyncToken?.elements) {
81
+ const elements = data.data.messengerMessagesBySyncToken.elements;
82
+ console.log(`[GetNewMessages] Found messengerMessagesBySyncToken with ${elements.length} messages`);
83
+
84
+ let addedCount = 0;
85
+ let duplicateCount = 0;
86
+
87
+ elements.forEach(msg => {
88
+ const senderInfo = msg.sender || msg.actor;
89
+ const senderMember = senderInfo?.participantType?.member;
90
+ const authorId = senderInfo?.backendUrn || 'unknown';
91
+ const authorName = senderMember
92
+ ? `${senderMember.firstName?.text || ''} ${senderMember.lastName?.text || ''}`.trim()
93
+ : 'Unknown';
94
+
95
+ const hostIdentityUrn = senderInfo?.hostIdentityUrn || '';
96
+ const authorUrl = hostIdentityUrn.includes('urn:li:fsd_profile:')
97
+ ? hostIdentityUrn.split('urn:li:fsd_profile:')[1]
98
+ : '';
99
+
100
+ const conversationId = msg.backendConversationUrn || '';
101
+ const isSelf = senderMember?.distance === 'SELF';
102
+
103
+ const messageText = msg.body?.text || '';
104
+ const deliveredAt = msg.deliveredAt;
105
+
106
+ const messageKey = `${conversationId}-${deliveredAt}-${messageText}`;
107
+
108
+ if (messageText && deliveredAt && !processedMessages.has(messageKey)) {
109
+ processedMessages.add(messageKey);
110
+ allMessageEntities.push({
111
+ authorId: authorId,
112
+ authorName: authorName,
113
+ authorUrl: authorUrl,
114
+ conversationId: conversationId,
115
+ isSelf: isSelf,
116
+ messageText: messageText,
117
+ deliveredAt: deliveredAt
118
+ });
119
+ addedCount++;
120
+ } else if (processedMessages.has(messageKey)) {
121
+ duplicateCount++;
122
+ }
123
+ });
124
+
125
+ console.log(`[GetNewMessages] Added ${addedCount} new messages, ${duplicateCount} duplicates, total now: ${allMessageEntities.length}`);
126
+ }
127
+ } catch (error) {
128
+ console.error('[GetNewMessages] Error parsing response:', error);
129
+ }
130
+
131
+ try {
132
+ await route.fulfill({ response });
133
+ } catch (fulfillError) {
134
+ }
135
+ return;
136
+ }
137
+
138
+ await route.continue();
139
+ };
140
+
141
+ try {
142
+ await page.route('**/voyagerMessagingGraphQL/**', routeHandler);
143
+
144
+ await page.goto('https://www.linkedin.com/messaging/', {
145
+ waitUntil: 'domcontentloaded',
146
+ timeout: 60000
147
+ });
148
+
149
+ await page.waitForLoadState('domcontentloaded');
150
+ await new Promise(resolve => setTimeout(resolve, 2000));
151
+
152
+ console.log('[GetNewMessages] Waiting for conversations list to load...');
153
+
154
+ try {
155
+ await page.waitForSelector('.msg-conversations-container__conversations-list', {
156
+ timeout: 30000,
157
+ state: 'visible'
158
+ });
159
+ } catch (error) {
160
+ console.log('[GetNewMessages] Timeout waiting for conversations list');
161
+ try {
162
+ await page.unroute('**/voyagerMessagingGraphQL/**', routeHandler);
163
+ } catch (unrouteError) {
164
+ console.log('[GetNewMessages] Error unrouting (non-critical):', unrouteError.message);
165
+ }
166
+ return { newMessagesCount: 0, messages: [] };
167
+ }
168
+
169
+ await new Promise(resolve => setTimeout(resolve, 2000));
170
+
171
+ console.log('[GetNewMessages] Looking for conversations list...');
172
+ const conversationsList = await page.locator('.msg-conversations-container__conversations-list').first();
173
+ const listCount = await conversationsList.count();
174
+ console.log('[GetNewMessages] Conversations list count:', listCount);
175
+
176
+ if (listCount === 0) {
177
+ console.log('[GetNewMessages] No conversations list found, exiting');
178
+ try {
179
+ await page.unroute('**/voyagerMessagingGraphQL/**', routeHandler);
180
+ } catch (unrouteError) {
181
+ console.log('[GetNewMessages] Error unrouting (non-critical):', unrouteError.message);
182
+ }
183
+ return { newMessagesCount: 0, messages: [] };
184
+ }
185
+
186
+ console.log('[GetNewMessages] Waiting for conversation items to load...');
187
+ try {
188
+ await page.waitForSelector('li.msg-conversation-listitem .msg-conversation-listitem__link', {
189
+ timeout: 15000,
190
+ state: 'visible'
191
+ });
192
+ } catch (error) {
193
+ console.log('[GetNewMessages] No conversation items found');
194
+ try {
195
+ await page.unroute('**/voyagerMessagingGraphQL/**', routeHandler);
196
+ } catch (unrouteError) {
197
+ console.log('[GetNewMessages] Error unrouting (non-critical):', unrouteError.message);
198
+ }
199
+ return { newMessagesCount: 0, messages: [] };
200
+ }
201
+
202
+ await new Promise(resolve => setTimeout(resolve, 1000));
203
+ const conversationItems = await conversationsList.locator('li.msg-conversation-listitem .msg-conversation-listitem__link').all();
204
+ console.log('[GetNewMessages] Found', conversationItems.length, 'conversation items');
205
+
206
+ await new Promise(resolve => setTimeout(resolve, 2000));
207
+ const initialMessageCount = allMessageEntities.length;
208
+ console.log(`[GetNewMessages] Messages from initially opened conversation: ${initialMessageCount}`);
209
+
210
+ let shouldContinue = true;
211
+ for (let i = 0; i < conversationItems.length && shouldContinue; i++) {
212
+ const item = conversationItems[i];
213
+
214
+ try {
215
+ const messageCountBefore = allMessageEntities.length;
216
+
217
+ await item.scrollIntoViewIfNeeded();
218
+ await new Promise(resolve => setTimeout(resolve, 300));
219
+ await item.click({ timeout: 10000, force: false });
220
+ await new Promise(resolve => setTimeout(resolve, 2500));
221
+
222
+ const messageContainer = await page.locator('.msg-s-message-list.scrollable').first();
223
+ const containerExists = await messageContainer.count();
224
+
225
+ if (containerExists > 0) {
226
+ let reachedOldMessages = false;
227
+ let scrollAttempts = 0;
228
+ const maxScrollAttempts = 20;
229
+
230
+ await messageContainer.hover();
231
+ await new Promise(resolve => setTimeout(resolve, 500));
232
+
233
+ let noNewMessagesRetries = 0;
234
+
235
+ while (!reachedOldMessages && scrollAttempts < maxScrollAttempts) {
236
+ const beforeScrollCount = allMessageEntities.length;
237
+ console.log(`[GetNewMessages] Scroll cycle ${scrollAttempts + 1}, current messages: ${beforeScrollCount}`);
238
+
239
+
240
+ for (let i = 0; i < 5; i++) {
241
+ await messageContainer.evaluate(el => {
242
+ el.scrollBy({ top: -2000, behavior: 'smooth' });
243
+ });
244
+ console.log(`[GetNewMessages] Scrolled up -2000px (${i + 1}/5)`);
245
+ await new Promise(resolve => setTimeout(resolve, 1000));
246
+ }
247
+
248
+ console.log(`[GetNewMessages] Waiting for LinkedIn to load older messages...`);
249
+ await new Promise(resolve => setTimeout(resolve, 3000));
250
+
251
+ const afterScrollCount = allMessageEntities.length;
252
+ const newMessagesLoaded = afterScrollCount - beforeScrollCount;
253
+ console.log(`[GetNewMessages] After 5 scrolls: ${afterScrollCount} messages (${newMessagesLoaded} new)`);
254
+
255
+ if (newMessagesLoaded === 0) {
256
+ noNewMessagesRetries++;
257
+ console.log(`[GetNewMessages] No new messages loaded, retry ${noNewMessagesRetries}/2`);
258
+
259
+ if (noNewMessagesRetries >= 2) {
260
+ console.log('[GetNewMessages] No new messages after 2 retries, reached the top');
261
+ break;
262
+ }
263
+ } else {
264
+ noNewMessagesRetries = 0;
265
+ console.log(`[GetNewMessages] Loaded ${newMessagesLoaded} new messages, continuing...`);
266
+ }
267
+
268
+ const oldestMessage = allMessageEntities
269
+ .filter(msg => msg.deliveredAt)
270
+ .sort((a, b) => a.deliveredAt - b.deliveredAt)[0];
271
+
272
+ if (oldestMessage && oldestMessage.deliveredAt < lastCheckedTimestamp) {
273
+ reachedOldMessages = true;
274
+ console.log('[GetNewMessages] Reached messages older than lastChecked in this conversation');
275
+ }
276
+
277
+ scrollAttempts++;
278
+ }
279
+
280
+ console.log(`[GetNewMessages] Finished scrolling after ${scrollAttempts} attempts`);
281
+ }
282
+
283
+ const messageCountAfter = allMessageEntities.length;
284
+ const newMessages = messageCountAfter - messageCountBefore;
285
+ console.log(`[GetNewMessages] Total messages from conversation ${i + 1}: ${newMessages}`);
286
+
287
+ if (newMessages > 0) {
288
+ const conversationMessages = allMessageEntities.slice(messageCountBefore);
289
+ const latestInThisConversation = conversationMessages
290
+ .filter(msg => msg.deliveredAt)
291
+ .sort((a, b) => b.deliveredAt - a.deliveredAt)[0];
292
+
293
+ if (latestInThisConversation && latestInThisConversation.deliveredAt < lastCheckedTimestamp) {
294
+ console.log('[GetNewMessages] Latest message in conversation is older than lastChecked, stopping conversation loop');
295
+ shouldContinue = false;
296
+ }
297
+ }
298
+ } catch (error) {
299
+ console.error('[GetNewMessages] Error with conversation:', error.message);
300
+ }
301
+ }
302
+
303
+ await new Promise(resolve => setTimeout(resolve, 2000));
304
+
305
+ try {
306
+ await page.unroute('**/voyagerMessagingGraphQL/**', routeHandler);
307
+ } catch (error) {
308
+ console.log('[GetNewMessages] Error unrouting (non-critical):', error.message);
309
+ }
310
+
311
+ console.log('[GetNewMessages] Total messages collected:', allMessageEntities.length);
312
+ console.log('[GetNewMessages] Conversations with participants mapped:', conversationParticipantsMap.size);
313
+
314
+ const newMessages = allMessageEntities.filter(msg => msg.deliveredAt > lastCheckedTimestamp);
315
+ newMessages.sort((a, b) => a.deliveredAt - b.deliveredAt);
316
+ console.log('[GetNewMessages] New messages count:', newMessages.length);
317
+
318
+ const newMessagesByConversation = {};
319
+ newMessages.forEach(msg => {
320
+ if (!newMessagesByConversation[msg.conversationId]) {
321
+ newMessagesByConversation[msg.conversationId] = [];
322
+ }
323
+ newMessagesByConversation[msg.conversationId].push(msg);
324
+ });
325
+
326
+ console.log('[GetNewMessages] Conversations with new messages:', Object.keys(newMessagesByConversation).length);
327
+
328
+ const encodedUrlMap = new Map();
329
+ for (const conversationId in newMessagesByConversation) {
330
+ const encodedUrl = conversationParticipantsMap.get(conversationId);
331
+ if (encodedUrl) {
332
+ encodedUrlMap.set(encodedUrl, null);
333
+ } else {
334
+ console.log(`[GetNewMessages] No participant found for conversation:`, conversationId);
335
+ }
336
+ }
337
+ console.log('[GetNewMessages] Unique profiles to decode:', encodedUrlMap.size);
338
+
339
+ const decodedUrlMap = new Map();
340
+ for (const encodedUrl of encodedUrlMap.keys()) {
341
+ try {
342
+ await page.goto(`https://www.linkedin.com/in/${encodedUrl}`, {
343
+ waitUntil: 'domcontentloaded',
344
+ timeout: 30000
345
+ });
346
+ await page.waitForLoadState('domcontentloaded');
347
+ await new Promise(resolve => setTimeout(resolve, 1000));
348
+
349
+ const currentUrl = page.url();
350
+ const match = currentUrl.match(/linkedin\.com\/in\/([^\/\?]+)/);
351
+ if (match && match[1]) {
352
+ decodedUrlMap.set(encodedUrl, match[1]);
353
+ }
354
+ } catch (error) {
355
+ console.error(`[GetNewMessages] Failed to decode URL:`, error.message);
356
+ }
357
+ }
358
+
359
+ let sentCount = 0;
360
+ let failedCount = 0;
361
+ let totalToSend = 0;
362
+
363
+ for (const conversationId in newMessagesByConversation) {
364
+ const messages = newMessagesByConversation[conversationId];
365
+ const encodedUrl = conversationParticipantsMap.get(conversationId);
366
+ const linkedinUrl = encodedUrl ? decodedUrlMap.get(encodedUrl) : null;
367
+
368
+ if (!linkedinUrl) {
369
+ console.log(`[GetNewMessages] Skipping conversation ${conversationId} - could not find or decode other person's URL`);
370
+ continue;
371
+ }
372
+
373
+ console.log(`[GetNewMessages] Sending ${messages.length} messages to ${linkedinUrl}`);
374
+ totalToSend += messages.length;
375
+
376
+ for (const msg of messages) {
377
+ try {
378
+ const payload = {
379
+ linkedin_url: linkedinUrl,
380
+ message: msg.messageText,
381
+ is_receipent: !msg.isSelf,
382
+ timestamp: msg.deliveredAt
383
+ };
384
+
385
+ console.log(`[GetNewMessages] Posting message: "${msg.messageText.substring(0, 50)}..." (${msg.deliveredAt})`);
386
+
387
+ await axios.post('https://api.coffeeinabit.com/messages', payload, {
388
+ headers: {
389
+ 'Authorization': `Bearer ${accessToken}`,
390
+ 'Content-Type': 'application/json'
391
+ }
392
+ });
393
+
394
+ sentCount++;
395
+ } catch (error) {
396
+ failedCount++;
397
+ console.error(`[GetNewMessages] Failed to send message to ${linkedinUrl}: "${msg.messageText.substring(0, 50)}..."`);
398
+ console.error(`[GetNewMessages] Error:`, error.message);
399
+ if (error.response) {
400
+ console.error(`[GetNewMessages] Response status:`, error.response.status);
401
+ console.error(`[GetNewMessages] Response data:`, error.response.data);
402
+ }
403
+ }
404
+ }
405
+ }
406
+
407
+ console.log(`[GetNewMessages] Successfully sent ${sentCount}/${totalToSend} messages${failedCount > 0 ? ` (${failedCount} failed)` : ''}`);
408
+
409
+ return {
410
+ newMessagesCount: newMessages.length,
411
+ messages: newMessages
412
+ };
413
+
414
+ } catch (error) {
415
+ console.error('[GetNewMessages] Error:', error.message);
416
+ try {
417
+ await page.unroute('**/voyagerMessagingGraphQL/**', routeHandler);
418
+ } catch (unrouteError) {
419
+ console.log('[GetNewMessages] Error unrouting (non-critical):', unrouteError.message);
420
+ }
421
+ throw error;
422
+ }
423
+ }
@@ -0,0 +1,255 @@
1
+ export async function executeGetProfile(page, action) {
2
+ if (!page) {
3
+ throw new Error('No page available');
4
+ }
5
+
6
+ let username = action.parameters?.username;
7
+ if (!username) {
8
+ throw new Error('Missing username for get_profile');
9
+ }
10
+
11
+ try{
12
+ if (username === 'self') {
13
+ await page.goto('https://www.linkedin.com/feed/', {
14
+ waitUntil: 'domcontentloaded',
15
+ timeout: 60000
16
+ });
17
+
18
+ await page.waitForLoadState('domcontentloaded');
19
+ await new Promise(resolve => setTimeout(resolve, 2000));
20
+
21
+ const profileCard = await page.locator('.profile-card-member-details').first();
22
+ if (await profileCard.count() === 0) {
23
+ throw new Error('Could not find profile card on /feed');
24
+ }
25
+
26
+ const profileNameLink = await profileCard.locator('.profile-card-name').first();
27
+ if (await profileNameLink.count() === 0) {
28
+ throw new Error('Could not find profile name link on /feed');
29
+ }
30
+
31
+ await profileNameLink.click();
32
+ await page.waitForLoadState('domcontentloaded');
33
+ await new Promise(resolve => setTimeout(resolve, 2000));
34
+
35
+ const currentUrl = page.url();
36
+ const match = currentUrl.match(/\/in\/([^/]+)/);
37
+ if (!match) {
38
+ throw new Error('Could not extract username from profile URL');
39
+ }
40
+ username = match[1];
41
+ }
42
+
43
+ const profileUrl = `https://www.linkedin.com/in/${username}`;
44
+ await page.goto(profileUrl, {
45
+ waitUntil: 'domcontentloaded',
46
+ timeout: 60000
47
+ });
48
+
49
+ await page.waitForLoadState('domcontentloaded');
50
+ await new Promise(resolve => setTimeout(resolve, 2000));
51
+
52
+ for (let i = 0; i < 3; ++i) {
53
+ await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
54
+ await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 2000) + 500));
55
+ }
56
+ await page.evaluate(() => window.scrollTo(0, 0));
57
+ await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 1000) + 500));
58
+
59
+ const profileText = await page.evaluate(() => {
60
+ const mainContent = document.querySelector('main');
61
+ if (!mainContent) return '';
62
+ function getAllText(element) {
63
+ let text = '';
64
+ for (const node of element.childNodes) {
65
+ if (node.nodeType === 3) {
66
+ text += node.textContent.trim() + ' ';
67
+ } else if (node.nodeType === 1) {
68
+ text += getAllText(node) + ' ';
69
+ }
70
+ }
71
+ return text.trim();
72
+ }
73
+ return getAllText(mainContent);
74
+ });
75
+
76
+ const { moreButtonOptions, isFirstDegreeConnection } = await getDropdownOptions(page);
77
+
78
+ const recentActivityUrl = `https://www.linkedin.com/in/${username}/recent-activity/all/`;
79
+ let recentPosts = {};
80
+
81
+ try {
82
+ await page.goto(recentActivityUrl, {
83
+ waitUntil: 'domcontentloaded',
84
+ timeout: 60000
85
+ });
86
+
87
+ await page.waitForLoadState('domcontentloaded');
88
+ await new Promise(resolve => setTimeout(resolve, 2000));
89
+
90
+ for (let i = 0; i < 5; ++i) {
91
+ await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
92
+ await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 2000) + 500));
93
+ }
94
+
95
+ recentPosts = await page.evaluate(() => {
96
+ const mainContent = document.querySelector('main');
97
+ if (!mainContent) return {};
98
+ const postContainers = mainContent.querySelectorAll('article, [data-urn*="activity"], .feed-shared-update-v2, .feed-shared-text');
99
+ const posts = {};
100
+ let postIndex = 1;
101
+
102
+ if (postContainers.length === 0) {
103
+ const allElements = mainContent.querySelectorAll('*');
104
+ const textElements = [];
105
+ for (const element of allElements) {
106
+ const text = element.innerText || element.textContent || '';
107
+ if (text.length > 100 &&
108
+ text.includes('ago') &&
109
+ (text.includes('\u2022') || text.includes('Visible to')) &&
110
+ !text.includes('Like') &&
111
+ !text.includes('Comment') &&
112
+ !text.includes('Repost') &&
113
+ !text.includes('Send')) {
114
+ let cleanedText = text;
115
+ cleanedText = cleanedText.replace(/\d+\s*(Like|Comment|Repost|Send)/g, '');
116
+ cleanedText = cleanedText.replace(/\d+\s*comment/g, '');
117
+ cleanedText = cleanedText.replace(/Like\s*Comment\s*Repost\s*Send/g, '');
118
+ cleanedText = cleanedText.replace(/Activate to view larger image,/g, '');
119
+ cleanedText = cleanedText.replace(/\u2026more/g, '');
120
+ if (cleanedText.trim().length > 50) {
121
+ textElements.push(cleanedText.trim());
122
+ }
123
+ }
124
+ }
125
+ const uniquePosts = [...new Set(textElements)].slice(0, 5);
126
+ uniquePosts.forEach((post, index) => {
127
+ posts[index + 1] = { PostText: post, PostLink: null };
128
+ });
129
+ } else {
130
+ for (let i = 0; i < Math.min(5, postContainers.length); i++) {
131
+ const container = postContainers[i];
132
+ const text = container.innerText || container.textContent || '';
133
+ let postUrl = null;
134
+ let urn = container.getAttribute('data-urn');
135
+ if (!urn) {
136
+ const urnDiv = container.querySelector('[data-urn*="activity"]');
137
+ if (urnDiv) urn = urnDiv.getAttribute('data-urn');
138
+ }
139
+ if (urn && urn.startsWith('urn:li:activity:')) {
140
+ const activityId = urn.split(':').pop();
141
+ postUrl = `https://www.linkedin.com/feed/update/urn:li:activity:${activityId}`;
142
+ }
143
+ if (text.trim() && text.length > 50) {
144
+ posts[postIndex] = { PostText: text.trim(), PostLink: postUrl };
145
+ postIndex++;
146
+ }
147
+ }
148
+ }
149
+ return posts;
150
+ });
151
+
152
+ } catch (error) {
153
+ }
154
+
155
+ return {
156
+ profileText,
157
+ recentPosts,
158
+ moreButtonOptions,
159
+ isFirstDegreeConnection
160
+ };
161
+
162
+ } catch (error) {
163
+ console.error('[GetProfile] Error:', error.message);
164
+ throw error;
165
+ }
166
+ }
167
+
168
+ async function getDropdownOptions(page) {
169
+ let moreButtonOptions = [];
170
+ let isFirstDegreeConnection = null;
171
+
172
+ try {
173
+ const moreButton = await getButtonByText(page, 'More', true);
174
+
175
+ if (moreButton && await moreButton.isVisible()) {
176
+ try {
177
+ await moreButton.click();
178
+ } catch (error) {
179
+ try {
180
+ await moreButton.click();
181
+ } catch (error) {
182
+ }
183
+ }
184
+ await new Promise(resolve => setTimeout(resolve, 1000));
185
+
186
+ const dropdownOptions = await page.evaluate(() => {
187
+ const options = [];
188
+ const dropdownItems = document.querySelectorAll('[role="menuitem"], [role="option"], .artdeco-dropdown__content-inner li, .artdeco-dropdown__content-inner button');
189
+
190
+ dropdownItems.forEach((item, index) => {
191
+ const text = item.textContent?.trim() || item.innerText?.trim() || '';
192
+ const ariaLabel = item.getAttribute('aria-label') || '';
193
+ const title = item.getAttribute('title') || '';
194
+
195
+ if (text || ariaLabel || title) {
196
+ options.push({
197
+ index: index + 1,
198
+ text: text,
199
+ ariaLabel: ariaLabel,
200
+ title: title,
201
+ fullText: `${text} ${ariaLabel} ${title}`.trim()
202
+ });
203
+ }
204
+ });
205
+
206
+ return options;
207
+ });
208
+
209
+ moreButtonOptions = dropdownOptions;
210
+
211
+ const hasRemoveConnection = dropdownOptions.some(option =>
212
+ option.text.includes('Remove Connection') ||
213
+ option.ariaLabel.includes('Remove your connection')
214
+ );
215
+
216
+ if (hasRemoveConnection) {
217
+ isFirstDegreeConnection = true;
218
+ }
219
+ }
220
+ } catch (error) {
221
+ }
222
+
223
+ return { moreButtonOptions, isFirstDegreeConnection };
224
+ }
225
+
226
+ async function getButtonByText(page, text, inMain = false) {
227
+ let buttons = [];
228
+
229
+ if (inMain) {
230
+ const mainContent = page.locator('main');
231
+ buttons = await mainContent.locator('button').all();
232
+ const spans = await page.locator('div[role="button"] span').all();
233
+ buttons = [...buttons, ...spans];
234
+ } else {
235
+ buttons = await page.locator('button').all();
236
+ const spans = await page.locator('div[role="button"] span').all();
237
+ buttons = [...buttons, ...spans];
238
+ }
239
+
240
+ for (let i = 0; i < buttons.length; i++) {
241
+ const button = buttons[i];
242
+ try {
243
+ if (!(await button.isVisible())) {
244
+ continue;
245
+ }
246
+ const buttonText = await button.textContent() || await button.innerText() || '';
247
+ if (buttonText.trim() === text) {
248
+ return button;
249
+ }
250
+ } catch (error) {
251
+ }
252
+ }
253
+
254
+ return null;
255
+ }