coffeeinabit 0.0.8 → 0.0.10
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/package.json +1 -1
- package/tools/get_messages.js +65 -9
- package/tools/send_connection_request.js +47 -24
- package/tools/send_messages.js +93 -13
package/package.json
CHANGED
package/tools/get_messages.js
CHANGED
|
@@ -12,7 +12,7 @@ export async function executeGetMessages(page, action) {
|
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
await page.waitForLoadState('domcontentloaded');
|
|
15
|
-
await
|
|
15
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
16
16
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
17
17
|
|
|
18
18
|
await page.evaluate(() => { window.scrollTo(0, 0); });
|
|
@@ -50,6 +50,12 @@ export async function executeGetMessages(page, action) {
|
|
|
50
50
|
];
|
|
51
51
|
|
|
52
52
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
53
|
+
try {
|
|
54
|
+
const currentUrl = page.url();
|
|
55
|
+
if (currentUrl.includes('/messaging')) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {}
|
|
53
59
|
for (const selector of overlaySelectors) {
|
|
54
60
|
try {
|
|
55
61
|
const count = await page.locator(selector).count();
|
|
@@ -75,6 +81,49 @@ export async function executeGetMessages(page, action) {
|
|
|
75
81
|
};
|
|
76
82
|
|
|
77
83
|
const clickStrategies = [
|
|
84
|
+
async () => {
|
|
85
|
+
try {
|
|
86
|
+
const btn = page.locator('button:has-text("Message")').first();
|
|
87
|
+
if (await btn.isVisible().catch(() => false)) {
|
|
88
|
+
await btn.scrollIntoViewIfNeeded();
|
|
89
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
90
|
+
await btn.click({ timeout: 5000, force: false });
|
|
91
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
92
|
+
return await checkOverlayAppeared(page);
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {}
|
|
95
|
+
return false;
|
|
96
|
+
},
|
|
97
|
+
async () => {
|
|
98
|
+
try {
|
|
99
|
+
const btn = page.locator('button:has(svg[data-test-icon="send-privately-small"])').first();
|
|
100
|
+
if (await btn.isVisible().catch(() => false)) {
|
|
101
|
+
await btn.scrollIntoViewIfNeeded();
|
|
102
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
103
|
+
await btn.click({ timeout: 5000, force: false });
|
|
104
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
105
|
+
return await checkOverlayAppeared(page);
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {}
|
|
108
|
+
return false;
|
|
109
|
+
},
|
|
110
|
+
async () => {
|
|
111
|
+
try {
|
|
112
|
+
const classToken = 'DhBcSsKzUnyKdEOWwBJmWavDjlHFWuqygdMVO';
|
|
113
|
+
const candidates = await page.locator(`button.artdeco-button--primary.${classToken}`).all();
|
|
114
|
+
for (const btn of candidates.slice(0, 3)) {
|
|
115
|
+
const isVisible = await btn.isVisible().catch(() => false);
|
|
116
|
+
if (!isVisible) continue;
|
|
117
|
+
await btn.scrollIntoViewIfNeeded();
|
|
118
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
119
|
+
await btn.click({ timeout: 5000, force: false });
|
|
120
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
121
|
+
const ok = await checkOverlayAppeared(page);
|
|
122
|
+
if (ok) return true;
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {}
|
|
125
|
+
return false;
|
|
126
|
+
},
|
|
78
127
|
async () => {
|
|
79
128
|
try {
|
|
80
129
|
const buttons = await page.locator('button[aria-label^="Message"]').all();
|
|
@@ -246,14 +295,14 @@ export async function executeGetMessages(page, action) {
|
|
|
246
295
|
}
|
|
247
296
|
|
|
248
297
|
try {
|
|
249
|
-
await page.waitForSelector('.msg-
|
|
298
|
+
await page.waitForSelector('.msg-s-message-list-container', { timeout: 15000 });
|
|
250
299
|
} catch (error) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
300
|
+
try {
|
|
301
|
+
await page.waitForSelector('.msg-overlay-conversation-bubble__content-wrapper', { timeout: 5000 });
|
|
302
|
+
} catch (e) {
|
|
254
303
|
return {
|
|
255
304
|
action: 'get_messages',
|
|
256
|
-
result: { status: 'error', message: `Clicked Message
|
|
305
|
+
result: { status: 'error', message: `Clicked Message but no conversation view appeared for ${username}` },
|
|
257
306
|
status: 'failed'
|
|
258
307
|
};
|
|
259
308
|
}
|
|
@@ -309,13 +358,20 @@ export async function executeGetMessages(page, action) {
|
|
|
309
358
|
|
|
310
359
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
311
360
|
|
|
312
|
-
const content = await page.evaluate(() => {
|
|
361
|
+
const { content, items } = await page.evaluate(() => {
|
|
313
362
|
const messageList = document.querySelector('.msg-s-message-list');
|
|
314
363
|
if (messageList) {
|
|
315
|
-
|
|
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 };
|
|
316
368
|
}
|
|
317
369
|
const wrapper = document.querySelector('.msg-overlay-conversation-bubble__content-wrapper');
|
|
318
|
-
|
|
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 };
|
|
319
375
|
});
|
|
320
376
|
|
|
321
377
|
return {
|
|
@@ -38,7 +38,7 @@ async function detectConnectionStatus(page, username) {
|
|
|
38
38
|
moreActionsSelectors = await getButtonByText(page, 'More actions', inMain);
|
|
39
39
|
}
|
|
40
40
|
if (moreActionsSelectors && (await moreActionsSelectors.isVisible())) {
|
|
41
|
-
await moreActionsSelectors
|
|
41
|
+
await clickWithRetries(moreActionsSelectors);
|
|
42
42
|
await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 1000) + 500));
|
|
43
43
|
}
|
|
44
44
|
let removeConnectionButton = await getButtonByText(page, 'Remove Connection', inMain);
|
|
@@ -81,7 +81,7 @@ async function handleConnectButtonClick(page, username, inMain = true, handleSen
|
|
|
81
81
|
|
|
82
82
|
if (connectButton && (await connectButton.isVisible())) {
|
|
83
83
|
try {
|
|
84
|
-
await connectButton
|
|
84
|
+
await clickWithRetries(connectButton);
|
|
85
85
|
|
|
86
86
|
if (handleSendWithoutNote) {
|
|
87
87
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
@@ -96,7 +96,7 @@ async function handleConnectButtonClick(page, username, inMain = true, handleSen
|
|
|
96
96
|
|
|
97
97
|
if (sendWithoutNoteButton && (await sendWithoutNoteButton.isVisible())) {
|
|
98
98
|
try {
|
|
99
|
-
await sendWithoutNoteButton
|
|
99
|
+
await clickWithRetries(sendWithoutNoteButton);
|
|
100
100
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
101
101
|
return { status: 'success', message: `Connection request sent successfully to ${username}` };
|
|
102
102
|
} catch (error) {
|
|
@@ -123,33 +123,56 @@ async function handleConnectButtonClick(page, username, inMain = true, handleSen
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
async function getButtonByText(page, text, inMain = false) {
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
const candidates = Array.isArray(text) ? text : [text];
|
|
127
|
+
let elements = [];
|
|
128
128
|
if (inMain) {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
const main = page.locator('main');
|
|
130
|
+
elements = [
|
|
131
|
+
...(await main.locator('button').all()),
|
|
132
|
+
...(await main.locator('[role="button"]').all()),
|
|
133
|
+
...(await main.locator('[aria-label]').all())
|
|
134
|
+
];
|
|
133
135
|
} else {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
elements = [
|
|
137
|
+
...(await page.locator('button').all()),
|
|
138
|
+
...(await page.locator('[role="button"]').all()),
|
|
139
|
+
...(await page.locator('[aria-label]').all())
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
for (let i = 0; i < elements.length; i++) {
|
|
143
|
+
const el = elements[i];
|
|
141
144
|
try {
|
|
142
|
-
if (!(await
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
if (!(await el.isVisible())) continue;
|
|
146
|
+
const textContent = (await el.textContent()) || '';
|
|
147
|
+
const innerText = (await el.innerText()) || '';
|
|
148
|
+
const ariaLabel = (await el.getAttribute('aria-label')) || '';
|
|
149
|
+
const combined = `${textContent}\n${innerText}\n${ariaLabel}`.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
150
|
+
for (const c of candidates) {
|
|
151
|
+
const needle = String(c).toLowerCase().trim();
|
|
152
|
+
if (!needle) continue;
|
|
153
|
+
if (combined === needle || combined.includes(needle)) {
|
|
154
|
+
await el.scrollIntoViewIfNeeded();
|
|
155
|
+
return el;
|
|
156
|
+
}
|
|
148
157
|
}
|
|
149
|
-
} catch (
|
|
158
|
+
} catch (_) {
|
|
150
159
|
continue;
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
|
-
|
|
154
162
|
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function clickWithRetries(locator, retries = 3) {
|
|
166
|
+
for (let attempt = 0; attempt < retries; attempt++) {
|
|
167
|
+
try {
|
|
168
|
+
await locator.scrollIntoViewIfNeeded();
|
|
169
|
+
await locator.waitFor({ state: 'visible', timeout: 5000 });
|
|
170
|
+
await locator.click({ trial: false, timeout: 15000 });
|
|
171
|
+
return true;
|
|
172
|
+
} catch (_) {
|
|
173
|
+
await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
await locator.click();
|
|
177
|
+
return true;
|
|
155
178
|
}
|
package/tools/send_messages.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { humanLikeType } from './human_typing.js';
|
|
2
|
+
|
|
1
3
|
export async function executeSendMessages(page, action) {
|
|
2
4
|
const username = action.parameters?.username || action.username;
|
|
3
5
|
let messages = action.parameters?.messages || action.messages;
|
|
@@ -17,23 +19,93 @@ export async function executeSendMessages(page, action) {
|
|
|
17
19
|
});
|
|
18
20
|
|
|
19
21
|
await page.waitForLoadState('domcontentloaded');
|
|
22
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
20
23
|
await page.evaluate(() => { window.scrollTo(0, 300); });
|
|
21
24
|
await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 2000) + 500));
|
|
22
25
|
|
|
23
26
|
await closeAllConversationBubbles(page);
|
|
24
27
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const checkConversationOpened = async () => {
|
|
29
|
+
for (let i = 0; i < 10; i++) {
|
|
30
|
+
try {
|
|
31
|
+
const url = page.url();
|
|
32
|
+
if (url.includes('/messaging')) return true;
|
|
33
|
+
} catch (e) {}
|
|
34
|
+
try {
|
|
35
|
+
const cnt = await page.locator('.msg-s-message-list-container, .msg-overlay-conversation-bubble__content-wrapper').count();
|
|
36
|
+
if (cnt > 0) return true;
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
await new Promise(r => setTimeout(r, 500));
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const clickStrategies = [
|
|
44
|
+
async () => {
|
|
45
|
+
try {
|
|
46
|
+
const btn = page.locator('button:has-text("Message")').first();
|
|
47
|
+
if (await btn.isVisible().catch(() => false)) {
|
|
48
|
+
await btn.scrollIntoViewIfNeeded();
|
|
49
|
+
await new Promise(r => setTimeout(r, 300));
|
|
50
|
+
await btn.click({ timeout: 5000 });
|
|
51
|
+
await new Promise(r => setTimeout(r, 800));
|
|
52
|
+
return await checkConversationOpened();
|
|
53
|
+
}
|
|
54
|
+
} catch {}
|
|
55
|
+
return false;
|
|
56
|
+
},
|
|
57
|
+
async () => {
|
|
58
|
+
try {
|
|
59
|
+
const btn = page.locator('button:has(svg[data-test-icon="send-privately-small"])').first();
|
|
60
|
+
if (await btn.isVisible().catch(() => false)) {
|
|
61
|
+
await btn.scrollIntoViewIfNeeded();
|
|
62
|
+
await new Promise(r => setTimeout(r, 300));
|
|
63
|
+
await btn.click({ timeout: 5000 });
|
|
64
|
+
await new Promise(r => setTimeout(r, 800));
|
|
65
|
+
return await checkConversationOpened();
|
|
66
|
+
}
|
|
67
|
+
} catch {}
|
|
68
|
+
return false;
|
|
69
|
+
},
|
|
70
|
+
async () => {
|
|
71
|
+
try {
|
|
72
|
+
const classToken = 'DhBcSsKzUnyKdEOWwBJmWavDjlHFWuqygdMVO';
|
|
73
|
+
const candidates = await page.locator(`button.artdeco-button--primary.${classToken}`).all();
|
|
74
|
+
for (const btn of candidates.slice(0, 3)) {
|
|
75
|
+
const v = await btn.isVisible().catch(() => false);
|
|
76
|
+
if (!v) continue;
|
|
77
|
+
await btn.scrollIntoViewIfNeeded();
|
|
78
|
+
await new Promise(r => setTimeout(r, 200));
|
|
79
|
+
await btn.click({ timeout: 5000 });
|
|
80
|
+
await new Promise(r => setTimeout(r, 800));
|
|
81
|
+
if (await checkConversationOpened()) return true;
|
|
82
|
+
}
|
|
83
|
+
} catch {}
|
|
84
|
+
return false;
|
|
85
|
+
},
|
|
86
|
+
async () => {
|
|
87
|
+
try {
|
|
88
|
+
const buttons = await page.locator('button[aria-label^="Message"]').all();
|
|
89
|
+
for (const btn of buttons) {
|
|
90
|
+
const isVisible = await btn.isVisible().catch(() => false);
|
|
91
|
+
if (!isVisible) continue;
|
|
92
|
+
await btn.scrollIntoViewIfNeeded();
|
|
93
|
+
await new Promise(r => setTimeout(r, 300));
|
|
94
|
+
await btn.click({ timeout: 5000 });
|
|
95
|
+
await new Promise(r => setTimeout(r, 800));
|
|
96
|
+
if (await checkConversationOpened()) return true;
|
|
97
|
+
}
|
|
98
|
+
} catch {}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
let opened = false;
|
|
104
|
+
for (const s of clickStrategies) {
|
|
105
|
+
opened = await s();
|
|
106
|
+
if (opened) break;
|
|
32
107
|
}
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
await messageBtn.click();
|
|
36
|
-
} catch (error) {
|
|
108
|
+
if (!opened) {
|
|
37
109
|
return {
|
|
38
110
|
action: 'send_messages',
|
|
39
111
|
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.` },
|
|
@@ -46,8 +118,16 @@ export async function executeSendMessages(page, action) {
|
|
|
46
118
|
for (const message of messages) {
|
|
47
119
|
await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 100) + 500));
|
|
48
120
|
await new Promise(resolve => setTimeout(resolve, 200));
|
|
49
|
-
|
|
50
|
-
|
|
121
|
+
|
|
122
|
+
const editor = page.locator('.msg-form__contenteditable[contenteditable="true"]').first();
|
|
123
|
+
if (await editor.isVisible().catch(() => false)) {
|
|
124
|
+
await editor.click();
|
|
125
|
+
await new Promise(r => setTimeout(r, 150));
|
|
126
|
+
await humanLikeType(editor, message);
|
|
127
|
+
} else {
|
|
128
|
+
await findAndClickMessageInput(page);
|
|
129
|
+
await humanLikeType(page.locator('.msg-form__contenteditable[contenteditable="true"]').first(), message);
|
|
130
|
+
}
|
|
51
131
|
await new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * 500) + 500));
|
|
52
132
|
|
|
53
133
|
const clickedSend = await clickSendButton(page);
|