prospect-ai-agent 1.0.1 → 1.0.2
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/dist/executor/comment.js +98 -28
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/executor/comment.js
CHANGED
|
@@ -7,43 +7,113 @@ export async function executeComment(payload, page) {
|
|
|
7
7
|
log.job("comment", `Navigating to post: ${postUrl}`);
|
|
8
8
|
await page.goto(postUrl, { waitUntil: "domcontentloaded" });
|
|
9
9
|
await page.waitForTimeout(3000 + Math.random() * 2000);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
// On a single post page, the comment box may already be visible.
|
|
11
|
+
// If not, click the "Comment" action button to reveal it.
|
|
12
|
+
let commentBox = await findCommentBox(page);
|
|
13
|
+
if (!commentBox) {
|
|
14
|
+
log.job("comment", "Comment box not visible, clicking Comment button...");
|
|
15
|
+
const commentButton = page.locator('button[aria-label*="Comment" i], span:text-is("Comment")').first();
|
|
16
|
+
const btnVisible = await commentButton.isVisible().catch(() => false);
|
|
17
|
+
if (btnVisible) {
|
|
18
|
+
await commentButton.click();
|
|
19
|
+
await page.waitForTimeout(2000);
|
|
20
|
+
}
|
|
21
|
+
commentBox = await findCommentBox(page);
|
|
15
22
|
}
|
|
16
|
-
|
|
17
|
-
const boxVisible = await commentBox.isVisible().catch(() => false);
|
|
18
|
-
if (!boxVisible) {
|
|
23
|
+
if (!commentBox) {
|
|
19
24
|
return {
|
|
20
25
|
success: false,
|
|
21
|
-
error: "Comment box not found
|
|
26
|
+
error: "Comment box not found on this post page",
|
|
22
27
|
};
|
|
23
28
|
}
|
|
29
|
+
// Focus and type the comment character by character for reliability
|
|
24
30
|
await commentBox.click();
|
|
25
31
|
await page.waitForTimeout(500);
|
|
26
|
-
|
|
27
|
-
await page.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
// Clear any existing content first
|
|
33
|
+
await page.keyboard.press("Meta+a");
|
|
34
|
+
await page.waitForTimeout(200);
|
|
35
|
+
// Type character by character to work with contenteditable divs
|
|
36
|
+
await page.keyboard.type(comment, { delay: 30 + Math.random() * 20 });
|
|
37
|
+
await page.waitForTimeout(1500 + Math.random() * 1000);
|
|
38
|
+
log.job("comment", "Comment typed, looking for submit button...");
|
|
39
|
+
// LinkedIn's submit button inside the comment form - try multiple selectors
|
|
40
|
+
// The button text is "Post" (not "Comment") and it's inside the comment form
|
|
41
|
+
const submitSelectors = [
|
|
42
|
+
'form.comments-comment-box__form button.comments-comment-box__submit-button',
|
|
43
|
+
'button.comments-comment-box__submit-button',
|
|
44
|
+
'button.comments-comment-box-comment__submit-button',
|
|
45
|
+
'div.comments-comment-box button[type="submit"]',
|
|
46
|
+
'button:has-text("Post"):near(.ql-editor)',
|
|
47
|
+
];
|
|
48
|
+
let submitted = false;
|
|
49
|
+
for (const selector of submitSelectors) {
|
|
50
|
+
const btn = page.locator(selector).first();
|
|
51
|
+
const visible = await btn.isVisible().catch(() => false);
|
|
52
|
+
if (visible) {
|
|
53
|
+
const disabled = await btn.isDisabled().catch(() => true);
|
|
54
|
+
if (!disabled) {
|
|
55
|
+
log.job("comment", `Found submit button: ${selector}`);
|
|
56
|
+
await btn.scrollIntoViewIfNeeded().catch(() => { });
|
|
57
|
+
await page.waitForTimeout(300);
|
|
58
|
+
await btn.click({ force: true });
|
|
59
|
+
submitted = true;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
32
63
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
// Fallback: try Ctrl+Enter / Meta+Enter to submit
|
|
65
|
+
if (!submitted) {
|
|
66
|
+
log.job("comment", "No submit button found, trying Ctrl+Enter...");
|
|
67
|
+
await page.keyboard.press("Control+Enter");
|
|
68
|
+
submitted = true;
|
|
69
|
+
}
|
|
70
|
+
// Wait for the comment to be posted
|
|
71
|
+
await page.waitForTimeout(4000);
|
|
72
|
+
// Verify: check if the page shows our comment was posted
|
|
73
|
+
// After successful submission, LinkedIn clears the comment box
|
|
74
|
+
const boxAfter = await findCommentBox(page);
|
|
75
|
+
if (boxAfter) {
|
|
76
|
+
const remainingText = await boxAfter
|
|
77
|
+
.evaluate((el) => el.innerText.trim())
|
|
78
|
+
.catch(() => "");
|
|
79
|
+
if (remainingText.length > 0 && remainingText === comment) {
|
|
80
|
+
// Try one more time with force click on any visible Post/Submit button
|
|
81
|
+
log.job("comment", "First submit attempt may have failed, retrying...");
|
|
82
|
+
const retryBtn = page.locator('button:has-text("Post"), button[type="submit"]').last();
|
|
83
|
+
const retryVisible = await retryBtn.isVisible().catch(() => false);
|
|
84
|
+
if (retryVisible) {
|
|
85
|
+
await retryBtn.click({ force: true });
|
|
86
|
+
await page.waitForTimeout(4000);
|
|
87
|
+
}
|
|
88
|
+
const finalText = await boxAfter
|
|
89
|
+
.evaluate((el) => el.innerText.trim())
|
|
90
|
+
.catch(() => "");
|
|
91
|
+
if (finalText.length > 0 && finalText === comment) {
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
error: "Comment was typed but submit failed — text still in box after retries",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
46
98
|
}
|
|
47
99
|
log.success(`Comment posted on ${postUrl}`);
|
|
48
100
|
return { success: true };
|
|
49
101
|
}
|
|
102
|
+
async function findCommentBox(page) {
|
|
103
|
+
const selectors = [
|
|
104
|
+
'div.ql-editor[data-placeholder*="comment" i]',
|
|
105
|
+
'div.ql-editor[data-placeholder*="Add a comment" i]',
|
|
106
|
+
'.comments-comment-box__form .ql-editor',
|
|
107
|
+
'.comments-comment-texteditor .ql-editor',
|
|
108
|
+
'div[role="textbox"][contenteditable="true"][aria-label*="comment" i]',
|
|
109
|
+
'div.ql-editor[contenteditable="true"]',
|
|
110
|
+
];
|
|
111
|
+
for (const selector of selectors) {
|
|
112
|
+
const el = page.locator(selector).first();
|
|
113
|
+
const visible = await el.isVisible().catch(() => false);
|
|
114
|
+
if (visible) {
|
|
115
|
+
return el;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ApiClient } from "./api-client.js";
|
|
|
6
6
|
import { Agent } from "./agent.js";
|
|
7
7
|
import { runLogin } from "./login.js";
|
|
8
8
|
import { log } from "./logger.js";
|
|
9
|
-
const VERSION = "1.0.
|
|
9
|
+
const VERSION = "1.0.2";
|
|
10
10
|
const DEFAULT_SERVER = "http://localhost:3000";
|
|
11
11
|
const program = new Command();
|
|
12
12
|
program
|