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.
- package/cloud_auth.js +170 -0
- package/linkedin_automation.js +784 -0
- package/package.json +28 -0
- package/public/dashboard.html +533 -0
- package/public/login.html +163 -0
- package/public/styles.css +298 -0
- package/server.js +201 -0
- package/tools/check_connection_status.js +32 -0
- package/tools/comment_post.js +88 -0
- package/tools/cookie_manager.js +148 -0
- package/tools/get_daily_linkedin_connections.js +86 -0
- package/tools/get_linkedin_search_results.js +141 -0
- package/tools/get_linkedin_updates.js +51 -0
- package/tools/get_messages.js +92 -0
- package/tools/get_new_messages.js +423 -0
- package/tools/get_profile.js +255 -0
- package/tools/human_typing.js +32 -0
- package/tools/like_post.js +57 -0
- package/tools/send_connection_request.js +155 -0
- package/tools/send_messages.js +162 -0
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coffeeinabit",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CoffeeInABit App",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": ["automation", "linkedin", "windows", "playwright"],
|
|
8
|
+
"author": "Azam Alamov <azam.alamov@icloud.com>",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"bin": {
|
|
11
|
+
"coffeeinabit": "./server.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node server.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"axios": "^1.6.0",
|
|
18
|
+
"dotenv": "^16.3.1",
|
|
19
|
+
"express": "^4.18.2",
|
|
20
|
+
"express-session": "^1.17.3",
|
|
21
|
+
"playwright": "^1.40.0",
|
|
22
|
+
"session-file-store": "^1.5.0",
|
|
23
|
+
"socket.io": "^4.7.5"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"nodemon": "^3.0.1"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>CoffeeInABit - Dashboard</title>
|
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
8
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
9
|
+
<link href="/styles.css" rel="stylesheet">
|
|
10
|
+
</head>
|
|
11
|
+
<body class="bg-light">
|
|
12
|
+
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
|
13
|
+
<div class="container-fluid">
|
|
14
|
+
<a class="navbar-brand fw-bold" href="#">
|
|
15
|
+
<i class="bi bi-cup-hot-fill me-2"></i>
|
|
16
|
+
CoffeeInABit
|
|
17
|
+
</a>
|
|
18
|
+
<div class="navbar-nav ms-auto">
|
|
19
|
+
<div class="nav-item dropdown">
|
|
20
|
+
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
|
|
21
|
+
<i class="bi bi-person-circle me-2"></i>
|
|
22
|
+
<span>Profile</span>
|
|
23
|
+
</a>
|
|
24
|
+
<ul class="dropdown-menu dropdown-menu-end" style="min-width: 250px;">
|
|
25
|
+
<li class="px-3 py-2">
|
|
26
|
+
<small class="text-muted d-block mb-1">Signed in as</small>
|
|
27
|
+
<strong id="userEmail" class="d-block text-truncate" style="max-width: 220px;">Loading...</strong>
|
|
28
|
+
</li>
|
|
29
|
+
<li><hr class="dropdown-divider"></li>
|
|
30
|
+
<li class="px-3 py-2">
|
|
31
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
32
|
+
<div class="d-flex align-items-center">
|
|
33
|
+
<i class="bi bi-moon-stars me-2"></i>
|
|
34
|
+
<span>Dark Mode</span>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="form-check form-switch mb-0">
|
|
37
|
+
<input class="form-check-input" type="checkbox" role="switch" id="darkModeToggle" onchange="toggleDarkMode()">
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</li>
|
|
41
|
+
<li><hr class="dropdown-divider"></li>
|
|
42
|
+
<li class="px-3 py-2">
|
|
43
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
44
|
+
<div class="d-flex align-items-center">
|
|
45
|
+
<i class="bi bi-browser-chrome me-2"></i>
|
|
46
|
+
<span>Keep Browser</span>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="form-check form-switch mb-0">
|
|
49
|
+
<input class="form-check-input" type="checkbox" role="switch" id="keepBrowserToggle" onchange="toggleKeepBrowser()">
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</li>
|
|
53
|
+
<li><hr class="dropdown-divider"></li>
|
|
54
|
+
<li><a class="dropdown-item" href="#" onclick="logout()">
|
|
55
|
+
<i class="bi bi-box-arrow-right me-2"></i>Logout
|
|
56
|
+
</a></li>
|
|
57
|
+
</ul>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</nav>
|
|
62
|
+
|
|
63
|
+
<div class="container-fluid py-4">
|
|
64
|
+
<div class="row">
|
|
65
|
+
<div class="col-12">
|
|
66
|
+
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
67
|
+
<h1 class="h3 mb-0">
|
|
68
|
+
<i class="bi bi-speedometer2 me-2"></i>
|
|
69
|
+
Dashboard
|
|
70
|
+
</h1>
|
|
71
|
+
<div class="d-flex align-items-center gap-3">
|
|
72
|
+
<button id="startAutomationBtn" class="btn btn-primary" onclick="startAutomation()">
|
|
73
|
+
<i class="bi bi-play-fill me-2"></i>
|
|
74
|
+
Start Automation
|
|
75
|
+
</button>
|
|
76
|
+
<button id="stopAutomationBtn" class="btn btn-danger" onclick="stopAutomation()" style="display: none;">
|
|
77
|
+
<i class="bi bi-stop-fill me-2"></i>
|
|
78
|
+
Stop Automation
|
|
79
|
+
</button>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<!-- Browser State Container -->
|
|
84
|
+
<div class="row mb-4">
|
|
85
|
+
<div class="col-12">
|
|
86
|
+
<div class="card border-0 shadow-sm">
|
|
87
|
+
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
88
|
+
<h5 class="card-title mb-0">
|
|
89
|
+
<i class="bi bi-browser-chrome me-2"></i>
|
|
90
|
+
Browser State
|
|
91
|
+
</h5>
|
|
92
|
+
<div class="d-flex align-items-center gap-2">
|
|
93
|
+
<div id="automationStatus" class="badge bg-secondary">Idle</div>
|
|
94
|
+
<div id="currentActionBadge" class="badge bg-info" style="display: none;"></div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="card-body">
|
|
98
|
+
<div id="browserStateContainer">
|
|
99
|
+
<div class="text-center text-muted py-4">
|
|
100
|
+
<i class="bi bi-browser-chrome fs-1 d-block mb-3"></i>
|
|
101
|
+
<p class="mb-0">Click "Start Automation" to launch the browser</p>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
<div id="screenshotContainer" style="display: none;">
|
|
105
|
+
<button class="btn btn-link text-decoration-none p-0 mb-3 d-flex align-items-center" onclick="toggleBrowserView()" id="browserViewToggle">
|
|
106
|
+
<i class="bi bi-chevron-right me-2" id="browserViewChevron"></i>
|
|
107
|
+
<span>Show live browser view</span>
|
|
108
|
+
</button>
|
|
109
|
+
<div id="browserViewContent" style="display: none;">
|
|
110
|
+
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
111
|
+
<small class="text-muted" id="screenshotTimestamp">Last updated: --</small>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="text-center">
|
|
114
|
+
<img id="browserScreenshot" class="img-fluid border rounded shadow-sm" style="max-width: 100%; max-height: 400px;" alt="Browser Screenshot">
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="row">
|
|
124
|
+
<div class="col-12">
|
|
125
|
+
<div class="card border-0 shadow-sm">
|
|
126
|
+
<div class="card-header bg-white">
|
|
127
|
+
<h5 class="card-title mb-0">
|
|
128
|
+
<i class="bi bi-info-circle me-2"></i>
|
|
129
|
+
Account Information
|
|
130
|
+
</h5>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="card-body">
|
|
133
|
+
<div class="row">
|
|
134
|
+
<div class="col-md-6">
|
|
135
|
+
<p><strong>Email:</strong> <span id="userEmailInfo">Loading...</span></p>
|
|
136
|
+
<p><strong>User ID:</strong> <span id="userId">Loading...</span></p>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="col-md-6">
|
|
139
|
+
<p><strong>Status:</strong> <span class="badge bg-success">Active</span></p>
|
|
140
|
+
<p><strong>Last Login:</strong> <span id="lastLogin">Just now</span></p>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div id="toastContainer" class="toast-container"></div>
|
|
152
|
+
|
|
153
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
154
|
+
<script src="/socket.io/socket.io.js"></script>
|
|
155
|
+
<script>
|
|
156
|
+
const userEmail = document.getElementById('userEmail');
|
|
157
|
+
const userEmailInfo = document.getElementById('userEmailInfo');
|
|
158
|
+
const userId = document.getElementById('userId');
|
|
159
|
+
const toastContainer = document.getElementById('toastContainer');
|
|
160
|
+
const startAutomationBtn = document.getElementById('startAutomationBtn');
|
|
161
|
+
const stopAutomationBtn = document.getElementById('stopAutomationBtn');
|
|
162
|
+
const automationStatus = document.getElementById('automationStatus');
|
|
163
|
+
const browserStateContainer = document.getElementById('browserStateContainer');
|
|
164
|
+
|
|
165
|
+
const socket = io();
|
|
166
|
+
|
|
167
|
+
function initializeDarkMode() {
|
|
168
|
+
const darkModeEnabled = localStorage.getItem('darkMode') === 'true';
|
|
169
|
+
const darkModeToggle = document.getElementById('darkModeToggle');
|
|
170
|
+
|
|
171
|
+
if (darkModeEnabled) {
|
|
172
|
+
document.body.classList.add('dark-mode');
|
|
173
|
+
if (darkModeToggle) {
|
|
174
|
+
darkModeToggle.checked = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function toggleDarkMode() {
|
|
180
|
+
const darkModeToggle = document.getElementById('darkModeToggle');
|
|
181
|
+
const isDarkMode = darkModeToggle.checked;
|
|
182
|
+
|
|
183
|
+
if (isDarkMode) {
|
|
184
|
+
document.body.classList.add('dark-mode');
|
|
185
|
+
localStorage.setItem('darkMode', 'true');
|
|
186
|
+
} else {
|
|
187
|
+
document.body.classList.remove('dark-mode');
|
|
188
|
+
localStorage.setItem('darkMode', 'false');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function initializeKeepBrowser() {
|
|
193
|
+
const keepBrowserEnabled = localStorage.getItem('keepBrowser');
|
|
194
|
+
const keepBrowserToggle = document.getElementById('keepBrowserToggle');
|
|
195
|
+
|
|
196
|
+
if (keepBrowserEnabled === null) {
|
|
197
|
+
localStorage.setItem('keepBrowser', 'true');
|
|
198
|
+
if (keepBrowserToggle) {
|
|
199
|
+
keepBrowserToggle.checked = true;
|
|
200
|
+
}
|
|
201
|
+
} else if (keepBrowserEnabled === 'true') {
|
|
202
|
+
if (keepBrowserToggle) {
|
|
203
|
+
keepBrowserToggle.checked = true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function toggleKeepBrowser() {
|
|
209
|
+
const keepBrowserToggle = document.getElementById('keepBrowserToggle');
|
|
210
|
+
const isKeepBrowser = keepBrowserToggle.checked;
|
|
211
|
+
|
|
212
|
+
if (isKeepBrowser) {
|
|
213
|
+
localStorage.setItem('keepBrowser', 'true');
|
|
214
|
+
showAlert('info', 'Browser will be visible when automation starts');
|
|
215
|
+
} else {
|
|
216
|
+
localStorage.setItem('keepBrowser', 'false');
|
|
217
|
+
showAlert('info', 'Browser will run in headless mode');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function toggleBrowserView() {
|
|
222
|
+
const browserViewContent = document.getElementById('browserViewContent');
|
|
223
|
+
const browserViewChevron = document.getElementById('browserViewChevron');
|
|
224
|
+
const browserViewToggle = document.getElementById('browserViewToggle');
|
|
225
|
+
|
|
226
|
+
if (browserViewContent.style.display === 'none') {
|
|
227
|
+
browserViewContent.style.display = 'block';
|
|
228
|
+
browserViewChevron.classList.remove('bi-chevron-right');
|
|
229
|
+
browserViewChevron.classList.add('bi-chevron-down');
|
|
230
|
+
browserViewToggle.querySelector('span').textContent = 'Hide live browser view';
|
|
231
|
+
} else {
|
|
232
|
+
browserViewContent.style.display = 'none';
|
|
233
|
+
browserViewChevron.classList.remove('bi-chevron-down');
|
|
234
|
+
browserViewChevron.classList.add('bi-chevron-right');
|
|
235
|
+
browserViewToggle.querySelector('span').textContent = 'Show live browser view';
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function checkAuthStatus() {
|
|
240
|
+
try {
|
|
241
|
+
const response = await fetch('/auth/status');
|
|
242
|
+
const data = await response.json();
|
|
243
|
+
|
|
244
|
+
if (!data.authenticated) {
|
|
245
|
+
window.location.href = '/login';
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (data.user) {
|
|
250
|
+
userEmail.textContent = data.user.email || 'User';
|
|
251
|
+
userEmailInfo.textContent = data.user.email || 'Unknown';
|
|
252
|
+
userId.textContent = data.user.userId || 'Unknown';
|
|
253
|
+
}
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error('Error checking auth status:', error);
|
|
256
|
+
window.location.href = '/login';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function logout() {
|
|
261
|
+
try {
|
|
262
|
+
const response = await fetch('/auth/logout', {
|
|
263
|
+
method: 'POST',
|
|
264
|
+
headers: {
|
|
265
|
+
'Content-Type': 'application/json'
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
if (response.ok) {
|
|
270
|
+
window.location.href = '/login';
|
|
271
|
+
} else {
|
|
272
|
+
showAlert('error', 'Logout failed. Please try again.');
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
275
|
+
showAlert('error', 'Logout failed: ' + error.message);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function showAlert(type, message) {
|
|
280
|
+
const icons = {
|
|
281
|
+
success: 'bi-check-circle-fill',
|
|
282
|
+
error: 'bi-exclamation-circle-fill',
|
|
283
|
+
info: 'bi-info-circle-fill',
|
|
284
|
+
warning: 'bi-exclamation-triangle-fill'
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const titles = {
|
|
288
|
+
success: 'Success',
|
|
289
|
+
error: 'Error',
|
|
290
|
+
info: 'Info',
|
|
291
|
+
warning: 'Warning'
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const toastId = 'toast-' + Date.now();
|
|
295
|
+
const toast = document.createElement('div');
|
|
296
|
+
toast.className = `toast-notification ${type}`;
|
|
297
|
+
toast.id = toastId;
|
|
298
|
+
toast.innerHTML = `
|
|
299
|
+
<i class="bi ${icons[type] || icons.info} toast-icon"></i>
|
|
300
|
+
<div class="toast-content">
|
|
301
|
+
<strong>${titles[type] || titles.info}</strong>
|
|
302
|
+
<p>${message}</p>
|
|
303
|
+
</div>
|
|
304
|
+
<button class="toast-close" onclick="removeToast('${toastId}')">
|
|
305
|
+
<i class="bi bi-x"></i>
|
|
306
|
+
</button>
|
|
307
|
+
`;
|
|
308
|
+
|
|
309
|
+
toastContainer.appendChild(toast);
|
|
310
|
+
|
|
311
|
+
setTimeout(() => {
|
|
312
|
+
removeToast(toastId);
|
|
313
|
+
}, 6000);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function removeToast(toastId) {
|
|
317
|
+
const toast = document.getElementById(toastId);
|
|
318
|
+
if (toast) {
|
|
319
|
+
toast.classList.add('removing');
|
|
320
|
+
setTimeout(() => {
|
|
321
|
+
toast.remove();
|
|
322
|
+
}, 300);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
327
|
+
const loginStatus = urlParams.get('login');
|
|
328
|
+
|
|
329
|
+
if (loginStatus === 'success') {
|
|
330
|
+
showAlert('success', 'Welcome back! You have successfully logged in.');
|
|
331
|
+
history.replaceState({}, document.title, window.location.pathname);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function startAutomation() {
|
|
335
|
+
try {
|
|
336
|
+
startAutomationBtn.disabled = true;
|
|
337
|
+
startAutomationBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Starting...';
|
|
338
|
+
|
|
339
|
+
const keepBrowser = localStorage.getItem('keepBrowser') !== 'false';
|
|
340
|
+
|
|
341
|
+
const response = await fetch('/api/automation/start', {
|
|
342
|
+
method: 'POST',
|
|
343
|
+
headers: {
|
|
344
|
+
'Content-Type': 'application/json'
|
|
345
|
+
},
|
|
346
|
+
body: JSON.stringify({
|
|
347
|
+
headless: !keepBrowser
|
|
348
|
+
})
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const data = await response.json();
|
|
352
|
+
|
|
353
|
+
if (data.success) {
|
|
354
|
+
showAlert('success', 'Automation started! Browser is launching...');
|
|
355
|
+
updateAutomationUI('starting');
|
|
356
|
+
} else {
|
|
357
|
+
showAlert('error', data.error || 'Failed to start automation');
|
|
358
|
+
startAutomationBtn.disabled = false;
|
|
359
|
+
startAutomationBtn.innerHTML = '<i class="bi bi-play-fill me-2"></i>Start Automation';
|
|
360
|
+
}
|
|
361
|
+
} catch (error) {
|
|
362
|
+
showAlert('error', 'Error starting automation: ' + error.message);
|
|
363
|
+
startAutomationBtn.disabled = false;
|
|
364
|
+
startAutomationBtn.innerHTML = '<i class="bi bi-play-fill me-2"></i>Start Automation';
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function stopAutomation() {
|
|
369
|
+
try {
|
|
370
|
+
const response = await fetch('/api/automation/stop', {
|
|
371
|
+
method: 'POST',
|
|
372
|
+
headers: {
|
|
373
|
+
'Content-Type': 'application/json'
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const data = await response.json();
|
|
378
|
+
|
|
379
|
+
if (data.success) {
|
|
380
|
+
showAlert('success', 'Automation stopped');
|
|
381
|
+
updateAutomationUI('stopped');
|
|
382
|
+
} else {
|
|
383
|
+
showAlert('error', data.error || 'Failed to stop automation');
|
|
384
|
+
}
|
|
385
|
+
} catch (error) {
|
|
386
|
+
showAlert('error', 'Error stopping automation: ' + error.message);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function updateAutomationUI(status) {
|
|
391
|
+
switch (status) {
|
|
392
|
+
case 'starting':
|
|
393
|
+
automationStatus.textContent = 'Starting';
|
|
394
|
+
automationStatus.className = 'badge bg-warning';
|
|
395
|
+
startAutomationBtn.style.display = 'none';
|
|
396
|
+
stopAutomationBtn.style.display = 'inline-block';
|
|
397
|
+
break;
|
|
398
|
+
case 'running':
|
|
399
|
+
automationStatus.textContent = 'Running';
|
|
400
|
+
automationStatus.className = 'badge bg-info';
|
|
401
|
+
break;
|
|
402
|
+
case 'manual_login_needed':
|
|
403
|
+
automationStatus.textContent = 'Manual Login Required';
|
|
404
|
+
automationStatus.className = 'badge bg-warning';
|
|
405
|
+
updateBrowserState('Opening browser for manual login...', 'https://www.linkedin.com/login');
|
|
406
|
+
showAlert('warning', 'Manual login needed. Browser will open for you to login.');
|
|
407
|
+
break;
|
|
408
|
+
case 'waiting_for_login':
|
|
409
|
+
automationStatus.textContent = 'Waiting for Login';
|
|
410
|
+
automationStatus.className = 'badge bg-warning';
|
|
411
|
+
updateBrowserState('Waiting for LinkedIn login...', 'https://www.linkedin.com/login');
|
|
412
|
+
break;
|
|
413
|
+
case 'switching_to_headless':
|
|
414
|
+
automationStatus.textContent = 'Switching to Headless';
|
|
415
|
+
automationStatus.className = 'badge bg-info';
|
|
416
|
+
updateBrowserState('Switching browser to headless mode...', 'https://www.linkedin.com/feed');
|
|
417
|
+
showAlert('info', 'Login successful! Switching browser to headless mode...');
|
|
418
|
+
break;
|
|
419
|
+
case 'linkedin_logged_in':
|
|
420
|
+
automationStatus.textContent = 'LinkedIn Connected';
|
|
421
|
+
automationStatus.className = 'badge bg-success';
|
|
422
|
+
updateBrowserState('Successfully logged into LinkedIn!', 'https://www.linkedin.com/feed');
|
|
423
|
+
updateCurrentAction(null);
|
|
424
|
+
break;
|
|
425
|
+
case 'stopped':
|
|
426
|
+
automationStatus.textContent = 'Stopped';
|
|
427
|
+
automationStatus.className = 'badge bg-secondary';
|
|
428
|
+
startAutomationBtn.style.display = 'inline-block';
|
|
429
|
+
startAutomationBtn.disabled = false;
|
|
430
|
+
startAutomationBtn.innerHTML = '<i class="bi bi-play-fill me-2"></i>Start Automation';
|
|
431
|
+
stopAutomationBtn.style.display = 'none';
|
|
432
|
+
resetBrowserState();
|
|
433
|
+
break;
|
|
434
|
+
case 'error':
|
|
435
|
+
automationStatus.textContent = 'Error';
|
|
436
|
+
automationStatus.className = 'badge bg-danger';
|
|
437
|
+
startAutomationBtn.style.display = 'inline-block';
|
|
438
|
+
startAutomationBtn.disabled = false;
|
|
439
|
+
startAutomationBtn.innerHTML = '<i class="bi bi-play-fill me-2"></i>Start Automation';
|
|
440
|
+
stopAutomationBtn.style.display = 'none';
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function updateBrowserState(title, url) {
|
|
446
|
+
browserStateContainer.innerHTML = `
|
|
447
|
+
<div class="d-flex align-items-center">
|
|
448
|
+
<div class="flex-shrink-0 me-3">
|
|
449
|
+
<i class="bi bi-browser-chrome fs-2 text-primary"></i>
|
|
450
|
+
</div>
|
|
451
|
+
<div class="flex-grow-1">
|
|
452
|
+
<h6 class="mb-1">${title}</h6>
|
|
453
|
+
<p class="mb-0 text-muted small">
|
|
454
|
+
<i class="bi bi-link-45deg me-1"></i>
|
|
455
|
+
<a href="${url}" target="_blank" class="text-decoration-none">${url}</a>
|
|
456
|
+
</p>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
`;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function resetBrowserState() {
|
|
463
|
+
browserStateContainer.innerHTML = `
|
|
464
|
+
<div class="text-center text-muted py-4">
|
|
465
|
+
<i class="bi bi-browser-chrome fs-1 d-block mb-3"></i>
|
|
466
|
+
<p class="mb-0">Click "Start Automation" to launch the browser</p>
|
|
467
|
+
</div>
|
|
468
|
+
`;
|
|
469
|
+
document.getElementById('screenshotContainer').style.display = 'none';
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function updateScreenshot(base64Screenshot, url, title, timestamp) {
|
|
473
|
+
const screenshotContainer = document.getElementById('screenshotContainer');
|
|
474
|
+
const screenshotImg = document.getElementById('browserScreenshot');
|
|
475
|
+
const timestampEl = document.getElementById('screenshotTimestamp');
|
|
476
|
+
|
|
477
|
+
if (base64Screenshot) {
|
|
478
|
+
screenshotImg.src = `data:image/png;base64,${base64Screenshot}`;
|
|
479
|
+
screenshotImg.alt = `Screenshot of ${title}`;
|
|
480
|
+
timestampEl.textContent = `Last updated: ${new Date(timestamp).toLocaleTimeString()}`;
|
|
481
|
+
screenshotContainer.style.display = 'block';
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function updateCurrentAction(action) {
|
|
486
|
+
const actionBadge = document.getElementById('currentActionBadge');
|
|
487
|
+
|
|
488
|
+
if (action) {
|
|
489
|
+
const actionType = action.action || action.type || 'unknown';
|
|
490
|
+
const actionId = action.action_id || action.id || 'unknown';
|
|
491
|
+
const username = action.parameters?.username || 'N/A';
|
|
492
|
+
|
|
493
|
+
actionBadge.textContent = `Executing: ${actionType} (${username}) - ID: ${actionId}`;
|
|
494
|
+
actionBadge.style.display = 'inline-block';
|
|
495
|
+
actionBadge.className = 'badge bg-warning';
|
|
496
|
+
|
|
497
|
+
console.log('[Dashboard] Current action:', {
|
|
498
|
+
type: actionType,
|
|
499
|
+
id: actionId,
|
|
500
|
+
username: username,
|
|
501
|
+
parameters: action.parameters
|
|
502
|
+
});
|
|
503
|
+
} else {
|
|
504
|
+
actionBadge.textContent = 'Polling for actions...';
|
|
505
|
+
actionBadge.style.display = 'inline-block';
|
|
506
|
+
actionBadge.className = 'badge bg-info';
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
socket.on('automation_status', (data) => {
|
|
511
|
+
console.log('Automation status update:', data);
|
|
512
|
+
updateAutomationUI(data.status);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
socket.on('page_update', (data) => {
|
|
516
|
+
console.log('Page update:', data);
|
|
517
|
+
updateBrowserState(data.title, data.url);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
socket.on('screenshot_update', (data) => {
|
|
521
|
+
updateScreenshot(data.screenshot, data.url, data.title, data.timestamp);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
socket.on('action_update', (data) => {
|
|
525
|
+
updateCurrentAction(data.currentAction);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
initializeDarkMode();
|
|
529
|
+
initializeKeepBrowser();
|
|
530
|
+
checkAuthStatus();
|
|
531
|
+
</script>
|
|
532
|
+
</body>
|
|
533
|
+
</html>
|