coffeeinabit 0.0.11 → 0.0.13

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.
@@ -1,15 +1,21 @@
1
1
  export async function executeGetMessages(page, action) {
2
+ console.log('[get_messages] Starting executeGetMessages');
2
3
  const username = action.parameters?.username || action.username;
3
4
 
5
+ console.log('[get_messages] Username:', username);
6
+
4
7
  if (!username) {
8
+ console.error('[get_messages] Missing username');
5
9
  throw new Error('Missing username for get_messages');
6
10
  }
7
11
 
8
12
  const profileUrl = `https://www.linkedin.com/in/${username}`;
13
+ console.log('[get_messages] Navigating to profile:', profileUrl);
9
14
  await page.goto(profileUrl, {
10
15
  waitUntil: 'load',
11
16
  timeout: Math.floor(Math.random() * 20000) + 40000
12
17
  });
18
+ console.log('[get_messages] Page loaded, URL:', page.url());
13
19
 
14
20
  await page.waitForLoadState('domcontentloaded');
15
21
  await new Promise(resolve => setTimeout(resolve, 1000));
@@ -19,7 +25,10 @@ export async function executeGetMessages(page, action) {
19
25
  await new Promise(resolve => setTimeout(resolve, 500));
20
26
  await page.evaluate(() => { window.scrollTo(0, 300); });
21
27
  await new Promise(resolve => setTimeout(resolve, 1000));
22
-
28
+
29
+ console.log('[get_messages] Closing all conversation bubbles');
30
+ await closeAllConversationBubbles(page);
31
+
23
32
  try {
24
33
  await page.waitForSelector('button[aria-label^="Message"], main button.artdeco-button--primary', {
25
34
  timeout: 5000,
@@ -27,18 +36,21 @@ export async function executeGetMessages(page, action) {
27
36
  });
28
37
  } catch (error) {
29
38
  }
30
-
39
+
31
40
  await new Promise(resolve => setTimeout(resolve, 500));
32
41
 
42
+ console.log('[get_messages] Checking for pending connection request');
33
43
  const pendingButton = await getButtonByText(page, 'Pending', true);
34
44
  if (pendingButton) {
45
+ console.log('[get_messages] Connection request is pending');
35
46
  return {
36
47
  action: 'get_messages',
37
48
  result: { status: 'connection_request_pending', message: `Connection request pending for ${username}. Not connected` },
38
49
  status: 'pending'
39
50
  };
40
51
  }
41
-
52
+
53
+ console.log('[get_messages] Trying to find and click Message button');
42
54
  let clicked = false;
43
55
 
44
56
  const checkOverlayAppeared = async (page) => {
@@ -87,8 +99,8 @@ export async function executeGetMessages(page, action) {
87
99
  if (await btn.isVisible().catch(() => false)) {
88
100
  await btn.scrollIntoViewIfNeeded();
89
101
  await new Promise(resolve => setTimeout(resolve, 300));
90
- await btn.click({ timeout: 5000, force: false });
91
- await new Promise(resolve => setTimeout(resolve, 1000));
102
+ await btn.click({ timeout: 5000 });
103
+ await new Promise(resolve => setTimeout(resolve, 800));
92
104
  return await checkOverlayAppeared(page);
93
105
  }
94
106
  } catch (error) {}
@@ -100,8 +112,8 @@ export async function executeGetMessages(page, action) {
100
112
  if (await btn.isVisible().catch(() => false)) {
101
113
  await btn.scrollIntoViewIfNeeded();
102
114
  await new Promise(resolve => setTimeout(resolve, 300));
103
- await btn.click({ timeout: 5000, force: false });
104
- await new Promise(resolve => setTimeout(resolve, 1000));
115
+ await btn.click({ timeout: 5000 });
116
+ await new Promise(resolve => setTimeout(resolve, 800));
105
117
  return await checkOverlayAppeared(page);
106
118
  }
107
119
  } catch (error) {}
@@ -112,14 +124,13 @@ export async function executeGetMessages(page, action) {
112
124
  const classToken = 'DhBcSsKzUnyKdEOWwBJmWavDjlHFWuqygdMVO';
113
125
  const candidates = await page.locator(`button.artdeco-button--primary.${classToken}`).all();
114
126
  for (const btn of candidates.slice(0, 3)) {
115
- const isVisible = await btn.isVisible().catch(() => false);
116
- if (!isVisible) continue;
127
+ const v = await btn.isVisible().catch(() => false);
128
+ if (!v) continue;
117
129
  await btn.scrollIntoViewIfNeeded();
118
130
  await new Promise(resolve => setTimeout(resolve, 200));
119
- await btn.click({ timeout: 5000, force: false });
131
+ await btn.click({ timeout: 5000 });
120
132
  await new Promise(resolve => setTimeout(resolve, 800));
121
- const ok = await checkOverlayAppeared(page);
122
- if (ok) return true;
133
+ if (await checkOverlayAppeared(page)) return true;
123
134
  }
124
135
  } catch (error) {}
125
136
  return false;
@@ -129,164 +140,34 @@ export async function executeGetMessages(page, action) {
129
140
  const buttons = await page.locator('button[aria-label^="Message"]').all();
130
141
  for (const btn of buttons) {
131
142
  const isVisible = await btn.isVisible().catch(() => false);
132
- const ariaLabel = await btn.getAttribute('aria-label').catch(() => '');
133
- if (isVisible && ariaLabel && ariaLabel.startsWith('Message')) {
134
- await btn.scrollIntoViewIfNeeded();
135
- await new Promise(resolve => setTimeout(resolve, 500));
136
- await btn.click({ timeout: 5000, force: false });
137
- await new Promise(resolve => setTimeout(resolve, 2000));
138
- return await checkOverlayAppeared(page);
139
- }
140
- }
141
- } catch (error) {
142
- }
143
- return false;
144
- },
145
- async () => {
146
- try {
147
- const buttons = await page.locator('main button[aria-label^="Message"]').all();
148
- for (const btn of buttons) {
149
- const isVisible = await btn.isVisible().catch(() => false);
150
- if (isVisible) {
151
- await btn.scrollIntoViewIfNeeded();
152
- await new Promise(resolve => setTimeout(resolve, 300));
153
- await btn.click({ timeout: 5000, force: false });
154
- await new Promise(resolve => setTimeout(resolve, 1000));
155
- return await checkOverlayAppeared(page);
156
- }
157
- }
158
- } catch (error) {
159
- }
160
- return false;
161
- },
162
- async () => {
163
- try {
164
- const buttons = await page.locator('main button.artdeco-button--primary').all();
165
- for (const btn of buttons) {
166
- try {
167
- const text = await btn.textContent().catch(() => '');
168
- const ariaLabel = await btn.getAttribute('aria-label').catch(() => '');
169
- const isVisible = await btn.isVisible().catch(() => false);
170
-
171
- if (isVisible && text && text.trim().includes('Message') &&
172
- (!text.includes('Pending')) &&
173
- (!ariaLabel || !ariaLabel.includes('Pending'))) {
174
- await btn.scrollIntoViewIfNeeded();
175
- await new Promise(resolve => setTimeout(resolve, 300));
176
- await btn.click({ timeout: 5000, force: false });
177
- await new Promise(resolve => setTimeout(resolve, 1000));
178
- return await checkOverlayAppeared(page);
179
- }
180
- } catch (error) {
181
- continue;
182
- }
183
- }
184
- } catch (error) {
185
- }
186
- return false;
187
- },
188
- async () => {
189
- try {
190
- const allButtons = await page.locator('main button').all();
191
- for (const btn of allButtons) {
192
- try {
193
- const text = await btn.textContent().catch(() => '');
194
- const ariaLabel = await btn.getAttribute('aria-label').catch(() => '');
195
- const isVisible = await btn.isVisible().catch(() => false);
196
-
197
- if (isVisible && ((ariaLabel && ariaLabel.startsWith('Message')) ||
198
- (text && text.includes('Message') && !text.includes('Pending')))) {
199
- await btn.scrollIntoViewIfNeeded();
200
- await new Promise(resolve => setTimeout(resolve, 300));
201
- await btn.click({ timeout: 5000, force: false });
202
- await new Promise(resolve => setTimeout(resolve, 1000));
203
- return await checkOverlayAppeared(page);
204
- }
205
- } catch (error) {
206
- continue;
207
- }
208
- }
209
- } catch (error) {
210
- }
211
- return false;
212
- },
213
- async () => {
214
- try {
215
- const buttonFound = await page.evaluate(() => {
216
- const buttons = Array.from(document.querySelectorAll('button'));
217
- for (const btn of buttons) {
218
- const ariaLabel = btn.getAttribute('aria-label') || '';
219
- const textContent = btn.textContent || '';
220
- const isVisible = btn.offsetParent !== null &&
221
- window.getComputedStyle(btn).visibility !== 'hidden' &&
222
- window.getComputedStyle(btn).display !== 'none';
223
-
224
- if (isVisible && (ariaLabel.startsWith('Message') ||
225
- (textContent.includes('Message') && !textContent.includes('Pending')))) {
226
- const rect = btn.getBoundingClientRect();
227
- if (rect.width > 0 && rect.height > 0) {
228
- return {
229
- found: true,
230
- x: rect.left + rect.width / 2,
231
- y: rect.top + rect.height / 2
232
- };
233
- }
234
- }
235
- }
236
- return { found: false };
237
- });
238
-
239
- if (buttonFound.found) {
240
- await page.mouse.click(buttonFound.x, buttonFound.y);
241
- await new Promise(resolve => setTimeout(resolve, 1000));
242
- return await checkOverlayAppeared(page);
243
- }
244
- } catch (error) {
245
- }
246
- return false;
247
- },
248
- async () => {
249
- try {
250
- const messageBtn = await getButtonByText(page, 'Message', true);
251
- if (messageBtn) {
252
- await messageBtn.scrollIntoViewIfNeeded();
253
- await new Promise(resolve => setTimeout(resolve, 300));
254
- await messageBtn.click({ timeout: 5000, force: false });
255
- await new Promise(resolve => setTimeout(resolve, 1000));
256
- return await checkOverlayAppeared(page);
257
- }
258
- } catch (error) {
259
- }
260
- return false;
261
- },
262
- async () => {
263
- try {
264
- const messageBtn = await getButtonByText(page, 'Message', false);
265
- if (messageBtn) {
266
- await messageBtn.scrollIntoViewIfNeeded();
143
+ if (!isVisible) continue;
144
+ await btn.scrollIntoViewIfNeeded();
267
145
  await new Promise(resolve => setTimeout(resolve, 300));
268
- await messageBtn.click({ timeout: 5000, force: false });
269
- await new Promise(resolve => setTimeout(resolve, 1000));
270
- return await checkOverlayAppeared(page);
146
+ await btn.click({ timeout: 5000 });
147
+ await new Promise(resolve => setTimeout(resolve, 800));
148
+ if (await checkOverlayAppeared(page)) return true;
271
149
  }
272
- } catch (error) {
273
- }
150
+ } catch (error) {}
274
151
  return false;
275
152
  }
276
153
  ];
277
154
 
278
- for (const strategy of clickStrategies) {
155
+ for (let i = 0; i < clickStrategies.length; i++) {
156
+ console.log('[get_messages] Trying strategy', i + 1);
279
157
  try {
280
- clicked = await strategy();
158
+ clicked = await clickStrategies[i]();
281
159
  if (clicked) {
160
+ console.log('[get_messages] Strategy', i + 1, 'succeeded, conversation opened');
282
161
  break;
283
162
  }
284
163
  } catch (error) {
164
+ console.log('[get_messages] Strategy', i + 1, 'error:', error.message);
285
165
  continue;
286
166
  }
287
167
  }
288
168
 
289
169
  if (!clicked) {
170
+ console.error('[get_messages] Failed to open conversation - could not find Message button');
290
171
  return {
291
172
  action: 'get_messages',
292
173
  result: { status: 'not_connected', message: `Could not find Message button on ${username}'s profile. This usually means you are not connected with this user on LinkedIn. You can only retrieve messages from users who are in your connections.` },
@@ -294,89 +175,81 @@ export async function executeGetMessages(page, action) {
294
175
  };
295
176
  }
296
177
 
297
- try {
298
- await page.waitForSelector('.msg-s-message-list-container', { timeout: 15000 });
299
- } catch (error) {
300
- try {
301
- await page.waitForSelector('.msg-overlay-conversation-bubble__content-wrapper', { timeout: 5000 });
302
- } catch (e) {
303
- return {
304
- action: 'get_messages',
305
- result: { status: 'error', message: `Clicked Message but no conversation view appeared for ${username}` },
306
- status: 'failed'
307
- };
308
- }
309
- }
178
+ console.log('[get_messages] Conversation opened successfully');
310
179
 
311
180
  await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 2000) + 1000));
312
-
313
- await page.evaluate(() => {
314
- const messageList = document.querySelector('.msg-s-message-list');
315
- if (messageList) {
316
- messageList.scrollTop = 0;
181
+
182
+ console.log('[get_messages] Looking for message input by aria-label');
183
+ const messageData = await page.evaluate(() => {
184
+ const messageInput = document.querySelector('[aria-label="Write a message…"]');
185
+ if (!messageInput) {
186
+ console.log('[get_messages] Message input not found');
187
+ return { found: false, content: '', items: [] };
317
188
  }
318
- });
319
- await new Promise(resolve => setTimeout(resolve, 1000));
320
-
321
- let previousScrollHeight = 0;
322
- let scrollAttempts = 0;
323
- const maxScrollAttempts = 10;
324
-
325
- while (scrollAttempts < maxScrollAttempts) {
326
- const scrollResult = await page.evaluate(() => {
327
- const messageList = document.querySelector('.msg-s-message-list');
328
- if (!messageList) return { shouldContinue: false, scrollHeight: 0 };
329
-
330
- const currentScrollHeight = messageList.scrollHeight;
331
- messageList.scrollTop = messageList.scrollHeight;
189
+
190
+ console.log('[get_messages] Found message input, traversing up parent chain');
191
+ let currentElement = messageInput.parentElement;
192
+ let level = 0;
193
+ const maxLevels = 20;
194
+
195
+ while (currentElement && level < maxLevels) {
196
+ level++;
197
+ const hasMessageListContainer = currentElement.classList.contains('msg-s-message-list-container');
332
198
 
333
- return {
334
- shouldContinue: true,
335
- scrollHeight: currentScrollHeight,
336
- scrollTop: messageList.scrollTop
337
- };
338
- });
339
-
340
- if (!scrollResult.shouldContinue) {
341
- break;
199
+ if (hasMessageListContainer) {
200
+ console.log('[get_messages] Found msg-s-message-list-container at level', level);
201
+ const content = currentElement.innerText || currentElement.textContent || '';
202
+ const ul = currentElement.querySelector('ul.msg-s-message-list-content');
203
+ const items = ul ? Array.from(ul.querySelectorAll('li')).map(li => (li.innerText || '').trim()).filter(Boolean) : [];
204
+ return { found: true, content: content.trim(), items: items };
205
+ }
206
+
207
+ currentElement = currentElement.parentElement;
342
208
  }
343
-
344
- await new Promise(resolve => setTimeout(resolve, 1500));
345
-
346
- const newScrollHeight = await page.evaluate(() => {
347
- const messageList = document.querySelector('.msg-s-message-list');
348
- return messageList ? messageList.scrollHeight : 0;
209
+
210
+ console.log('[get_messages] Reached max levels or no parent found, using last element');
211
+ if (currentElement) {
212
+ const content = currentElement.innerText || currentElement.textContent || '';
213
+ const ul = currentElement.querySelector('ul.msg-s-message-list-content');
214
+ const items = ul ? Array.from(ul.querySelectorAll('li')).map(li => (li.innerText || '').trim()).filter(Boolean) : [];
215
+ return { found: true, content: content.trim(), items: items };
216
+ }
217
+
218
+ return { found: false, content: '', items: [] };
219
+ });
220
+
221
+ if (!messageData.found) {
222
+ console.log('[get_messages] Fallback: trying to find message list directly');
223
+ const fallbackData = await page.evaluate(() => {
224
+ const messageList = document.querySelector('.msg-s-message-list-container');
225
+ if (messageList) {
226
+ const content = messageList.innerText || messageList.textContent || '';
227
+ const ul = messageList.querySelector('ul.msg-s-message-list-content');
228
+ const items = ul ? Array.from(ul.querySelectorAll('li')).map(li => (li.innerText || '').trim()).filter(Boolean) : [];
229
+ return { found: true, content: content.trim(), items: items };
230
+ }
231
+ return { found: false, content: '', items: [] };
349
232
  });
350
-
351
- if (newScrollHeight === previousScrollHeight) {
352
- break;
233
+
234
+ if (fallbackData.found) {
235
+ return {
236
+ action: 'get_messages',
237
+ result: { status: 'success', message: fallbackData.content, items: fallbackData.items },
238
+ status: 'success'
239
+ };
353
240
  }
354
-
355
- previousScrollHeight = newScrollHeight;
356
- scrollAttempts++;
241
+
242
+ return {
243
+ action: 'get_messages',
244
+ result: { status: 'error', message: `Could not find message container for ${username}` },
245
+ status: 'failed'
246
+ };
357
247
  }
358
-
359
- await new Promise(resolve => setTimeout(resolve, 1000));
360
-
361
- const { content, items } = await page.evaluate(() => {
362
- const messageList = document.querySelector('.msg-s-message-list');
363
- if (messageList) {
364
- const ul = messageList.querySelector('ul.msg-s-message-list-content');
365
- const lis = ul ? Array.from(ul.querySelectorAll('li')) : [];
366
- const texts = lis.map(li => (li.innerText || '').trim()).filter(Boolean);
367
- return { content: (messageList.innerText || ''), items: texts };
368
- }
369
- const wrapper = document.querySelector('.msg-overlay-conversation-bubble__content-wrapper');
370
- const list = wrapper ? wrapper.querySelector('.msg-s-message-list') : null;
371
- const ul = list ? list.querySelector('ul.msg-s-message-list-content') : null;
372
- const lis = ul ? Array.from(ul.querySelectorAll('li')) : [];
373
- const texts = lis.map(li => (li.innerText || '').trim()).filter(Boolean);
374
- return { content: (wrapper ? wrapper.innerText : ''), items: texts };
375
- });
376
-
248
+
249
+ console.log('[get_messages] Successfully extracted messages');
377
250
  return {
378
251
  action: 'get_messages',
379
- result: { status: 'success', message: content },
252
+ result: { status: 'success', message: messageData.content, items: messageData.items },
380
253
  status: 'success'
381
254
  };
382
255
  }
@@ -412,3 +285,23 @@ async function getButtonByText(page, text, inMain = false) {
412
285
 
413
286
  return null;
414
287
  }
288
+
289
+ async function closeAllConversationBubbles(page) {
290
+ console.log('[get_messages] Closing all conversation bubbles');
291
+ await page.evaluate(() => {
292
+ const allButtons = document.querySelectorAll('button');
293
+ const closeButtons = Array.from(allButtons).filter(btn => {
294
+ const useElement = btn.querySelector('svg use[href="#close-small"]');
295
+ return useElement !== null;
296
+ });
297
+ console.log('[get_messages] Found', closeButtons.length, 'close buttons');
298
+ closeButtons.forEach(btn => {
299
+ try {
300
+ btn.click();
301
+ } catch (e) {
302
+ console.log('[get_messages] Error clicking close button:', e.message);
303
+ }
304
+ });
305
+ });
306
+ await new Promise(resolve => setTimeout(resolve, 500));
307
+ }
@@ -1,8 +1,23 @@
1
- export async function humanLikeType(element, text) {
1
+ export async function humanLikeType(element, text, mistypeProbability = 0.05) {
2
2
  for (const char of text) {
3
+ const shouldMistype = Math.random() < mistypeProbability;
4
+
5
+ if (shouldMistype && char !== ' ' && char !== '\n') {
6
+ const wrongChar = getRandomMistypeChar(char);
7
+ await element.type(wrongChar, { delay: 0 });
8
+
9
+ const mistypeDelay = Math.floor(Math.random() * 200) + 50;
10
+ await new Promise(resolve => setTimeout(resolve, mistypeDelay));
11
+
12
+ await element.press('Backspace', { delay: 0 });
13
+
14
+ const correctionDelay = Math.floor(Math.random() * 200) + 50;
15
+ await new Promise(resolve => setTimeout(resolve, correctionDelay));
16
+ }
17
+
3
18
  await element.type(char, { delay: 0 });
4
19
 
5
- const delay = Math.floor(Math.random() * 100) + 50;
20
+ const delay = Math.floor(Math.random() * 200) + 50;
6
21
  await new Promise(resolve => setTimeout(resolve, delay));
7
22
  }
8
23
 
@@ -10,6 +25,26 @@ export async function humanLikeType(element, text) {
10
25
  await new Promise(resolve => setTimeout(resolve, finalDelay));
11
26
  }
12
27
 
28
+ function getRandomMistypeChar(correctChar) {
29
+ const alphabet = 'abcdefghijklmnopqrstuvwxyz';
30
+ const numbers = '012389';
31
+ const specialChars = '!@';
32
+
33
+ let pool = alphabet;
34
+ if (/\d/.test(correctChar)) {
35
+ pool = numbers;
36
+ } else if (/[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(correctChar)) {
37
+ pool = specialChars;
38
+ }
39
+
40
+ let wrongChar;
41
+ do {
42
+ wrongChar = pool[Math.floor(Math.random() * pool.length)];
43
+ } while (wrongChar === correctChar && pool.length > 1);
44
+
45
+ return wrongChar;
46
+ }
47
+
13
48
  export async function readLinkedInCredentials() {
14
49
  const fs = (await import('fs')).default;
15
50
  const path = (await import('path')).default;