Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linkedin pipe2 #899

Merged
merged 35 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pipes/linkedin_ai_assistant/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ IndexedDB/
WebStorage/
Cookies*
History*

# storage files
lib/storage/*.json
21 changes: 15 additions & 6 deletions pipes/linkedin_ai_assistant/app/api/harvest/start/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,32 @@ export async function POST(req: NextRequest) {
}

// Return detailed status messages based on the harvesting result
const message = result.weeklyLimitReached
? `weekly linkedin invitation limit reached, retrying at ${new Date(result.nextHarvestTime!).toLocaleString()}`
: result.connectionsSent === 0
? "no new connections found to harvest"
: `sent ${result.connectionsSent} connections.`;
let message = '';
if (result.weeklyLimitReached) {
message = `weekly limit reached, retrying at ${new Date(result.nextHarvestTime!).toLocaleString()}`;
} else if (result.dailyLimitReached) {
message = `daily limit of ${result.connectionsSent} connections reached, next harvest at ${new Date(result.nextHarvestTime!).toLocaleString()}`;
} else if (result.connectionsSent === 0) {
message = "no new connections found to harvest";
} else {
message = `sent ${result.connectionsSent} connections.`;
}

return NextResponse.json(
{
message,
weeklyLimitReached: result.weeklyLimitReached,
dailyLimitReached: result.dailyLimitReached,
connectionsSent: result.connectionsSent,
nextHarvestTime: result.nextHarvestTime,
},
{ status: 200 }
);
} catch (error: any) {
console.error('error starting harvesting:', error);
return NextResponse.json({ message: error.message.toLowerCase() }, { status: 500 });
return NextResponse.json(
{ message: error.message.toLowerCase() },
{ status: 500 }
);
}
}
34 changes: 18 additions & 16 deletions pipes/linkedin_ai_assistant/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,37 @@

import { useState } from "react";
import { LaunchLinkedInChromeSession } from "@/components/launch_linkedin_chrome_session";
import TemplateEditor from "@/components/settings_editor";
import template from "@/lib/storage/templates.json";
import { StartWorkflow } from "@/components/start_workflow";
import StateViewer from "@/components/state_viewer";
import { IntroRequester } from "@/components/intro-requester";
import { ReloadButton } from "@/components/reload_button";
import { HarvestClosestConnections } from "@/components/harvest";

export default function Home() {
const [loginStatus, setLoginStatus] = useState<'checking' | 'logged_in' | 'logged_out' | null>(null);

return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen w-full p-4 pb-20 gap-16 sm:p-8">
<main className="w-full max-w-[95vw] flex flex-col gap-8 row-start-2 items-center sm:items-start justify-center">
<div className="w-full flex justify-end">
</div>
<div className="min-h-screen w-full p-4 pb-20 sm:p-8">
<div className="space-y-1.5 mb-8">
<h1 className="text-2xl font-semibold tracking-tight">linkedin ai assistant</h1>
<p className="text-sm text-muted-foreground">automate your linkedin interactions with ai</p>
</div>

<div className="flex flex-col gap-2 mb-8">
<ReloadButton />
<LaunchLinkedInChromeSession
loginStatus={loginStatus}
setLoginStatus={setLoginStatus}
/>
{loginStatus === 'logged_in' && (
<>
</div>

{loginStatus === 'logged_in' && (
<div className="w-full space-y-6">
<h2 className="text-2xl font-semibold mb-6">workflows</h2>
<div className="space-y-6 text-lg">
<HarvestClosestConnections />
{/* <TemplateEditor initialTemplate={template} />
<StartWorkflow />
<StateViewer /> */}
</>
)}
</main>
<IntroRequester />
</div>
</div>
)}
</div>
);
}
86 changes: 57 additions & 29 deletions pipes/linkedin_ai_assistant/components/harvest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ export function HarvestClosestConnections() {
const [status, setStatus] = useState("");
const [nextHarvestTime, setNextHarvestTime] = useState<string | null>(null);
const [connectionsSent, setConnectionsSent] = useState(0);
const [dailyLimitReached, setDailyLimitReached] = useState(false);
const [weeklyLimitReached, setWeeklyLimitReached] = useState(false);

useEffect(() => {
// Update initial state
fetch("/api/harvest/status")
.then(res => res.json())
.then(data => {
setConnectionsSent(data.connectionsSent || 0);
setDailyLimitReached(data.dailyLimitReached || false);
setWeeklyLimitReached(data.weeklyLimitReached || false);
if (data.nextHarvestTime) {
setNextHarvestTime(data.nextHarvestTime);
if (new Date(data.nextHarvestTime) > new Date()) {
Expand All @@ -32,8 +36,16 @@ export function HarvestClosestConnections() {
.then(res => res.json())
.then(data => {
setConnectionsSent(data.connectionsSent || 0);
setIsRunning(data.isHarvesting || false);
if (!data.isHarvesting) {
setDailyLimitReached(data.dailyLimitReached || false);
setWeeklyLimitReached(data.weeklyLimitReached || false);
if (data.nextHarvestTime) {
setNextHarvestTime(data.nextHarvestTime);
if (new Date(data.nextHarvestTime) > new Date()) {
setStatus(`harvesting cooldown active until ${new Date(data.nextHarvestTime).toLocaleString()}`);
}
}
if (!data.isHarvesting && isRunning) {
setIsRunning(false);
setStatus("harvest process stopped");
}
})
Expand All @@ -45,30 +57,38 @@ export function HarvestClosestConnections() {
}, [isRunning]);

const startHarvesting = async () => {
setIsRunning(true);
setStatus("starting harvesting process...");

try {
setIsRunning(true);
setStatus("starting harvesting process...");

const response = await fetch("/api/harvest/start", {
method: "POST",
});

const data = await response.json();
console.log('response data:', data);
console.log('harvest start response:', data);

if (response.ok) {
setStatus(data.message?.toLowerCase() || 'unknown status');
setConnectionsSent(data.connectionsSent || 0);
setDailyLimitReached(data.dailyLimitReached || false);
setWeeklyLimitReached(data.weeklyLimitReached || false);
if (data.nextHarvestTime) {
setNextHarvestTime(data.nextHarvestTime);
}
setIsRunning(true);
} else {
setStatus(`${data.message?.toLowerCase() || 'unknown error'}`);
setIsRunning(false);
// Handle 429 without stopping the workflow
if (response.status === 429) {
setNextHarvestTime(data.nextHarvestTime);
setStatus(data.message?.toLowerCase() || 'rate limit reached');
} else {
setStatus(`error: ${data.message?.toLowerCase() || 'unknown error'}`);
setIsRunning(false);
}
}
} catch (error: any) {
console.error("failed to start harvesting:", error);
setStatus(`${error.message?.toLowerCase() || error.toString().toLowerCase()}`);
setStatus(`error: ${error.message?.toLowerCase() || error.toString().toLowerCase()}`);
setIsRunning(false);
}
};
Expand All @@ -93,32 +113,40 @@ export function HarvestClosestConnections() {

return (
<div className="flex flex-row items-center gap-4">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">
<div className="flex items-center gap-4">
<span className="text-lg font-medium">
harvest connections {connectionsSent > 0 && `(${connectionsSent})`}
</span>
{isRunning || (nextHarvestTime && new Date(nextHarvestTime) > new Date()) ? (
<button
onClick={stopHarvesting}
className="bg-red-600 text-white px-3 py-1.5 rounded-md text-sm hover:bg-red-700"
>
stop
</button>
) : (
<button
onClick={startHarvesting}
className="bg-black text-white px-3 py-1.5 rounded-md text-sm"
disabled={nextHarvestTime && new Date(nextHarvestTime) > new Date()}
>
start
</button>
)}
<div className="flex gap-2">
{!isRunning && (
<button
onClick={startHarvesting}
className="bg-black text-white px-4 py-2 rounded-md text-base"
>
start
</button>
)}
{isRunning && (
<button
onClick={stopHarvesting}
className="bg-red-600 text-white px-4 py-2 rounded-md text-base hover:bg-red-700"
>
stop
</button>
)}
</div>
</div>
{status && (
{isRunning && status && (
<span className="text-sm text-gray-500">
{status}
</span>
)}
{(dailyLimitReached || weeklyLimitReached) && nextHarvestTime && (
<span className="text-sm text-gray-500">
{dailyLimitReached && `daily limit reached, next harvest at ${new Date(nextHarvestTime).toLocaleString()}`}
{weeklyLimitReached && `weekly limit reached, next harvest at ${new Date(nextHarvestTime).toLocaleString()}`}
</span>
)}
</div>
);
}
Loading
Loading