Files
wehub-resource-sync 9b395f5cc3
Build Chrome Extension / build (push) Waiting to run
Trigger Website Rebuild (Docs Updated) / dispatch (push) Waiting to run
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:48 +08:00

93 lines
4.2 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { cli, Strategy } from '@jackwener/opencli/registry';
import { AuthRequiredError, CommandExecutionError } from '@jackwener/opencli/errors';
const LINKEDIN_DOMAIN = 'www.linkedin.com';
const SENT_URL = 'https://www.linkedin.com/mynetwork/invitation-manager/sent/';
function unwrapEvaluateResult(payload) {
if (payload && typeof payload === 'object' && 'data' in payload && 'session' in payload) return payload.data;
return payload;
}
function buildSentInvitationsScript() {
return String.raw`(() => {
const clean = (s) => String(s || '').replace(/[ ]/g, ' ').replace(/\s+/g, ' ').trim();
const text = document.body ? (document.body.innerText || '') : '';
const href = location.href;
const authRequired = /\b(sign in|log in|join linkedin)\b/i.test(text)
|| /linkedin\.com\/(login|checkpoint|authwall|uas)/i.test(href);
const warning = /captcha|verification required|unusual activity|account restricted|temporarily restricted|security check|checkpoint/i.test(text);
const cleanName = (value) => {
const first = String(value || '').split(/\n+/).map(clean).filter(Boolean)[0] || '';
return clean(first
.replace(/^(view\s+)?profile\s+of\s+/i, '')
.replace(/\s*(?:View profile|LinkedIn|Pending|Sent|Withdraw).*$/i, ''));
};
const cards = Array.from(document.querySelectorAll('li, div, section, article')).filter((el) => {
if (!el || el.offsetParent === null) return false;
const t = clean(el.innerText || el.textContent || '');
return t && /withdraw/i.test(t) && t.length < 1200;
});
const byName = new Map();
for (const card of cards) {
const raw = card.innerText || card.textContent || '';
if (!/withdraw/i.test(raw)) continue;
const lines = raw.split(/\n+/).map(clean).filter(Boolean);
const link = card.querySelector('a[href*="/in/"]');
const linkName = cleanName(link ? (link.innerText || link.textContent || link.getAttribute('aria-label') || '') : '');
const name = linkName
|| cleanName(lines.find((line) => !/^(pending|sent|withdraw|message|view profile|invitation|invited|ago|manage|received)\b/i.test(line)) || '');
if (!name) continue;
const hrefAttr = link ? (link.getAttribute('href') || '') : '';
const profile_url = hrefAttr ? new URL(hrefAttr, location.origin).toString().replace(/[?#].*$/, '') : '';
const invited_date_text = clean((raw.match(/(?:Sent|Invited)\s+(?:\d+\s+\w+\s+ago|yesterday|today)/i) || [''])[0]);
const key = name.toLowerCase();
const existing = byName.get(key);
if (!existing) {
byName.set(key, { name, profile_url, invited_date_text });
} else {
if (!existing.profile_url && profile_url) existing.profile_url = profile_url;
if (!existing.invited_date_text && invited_date_text) existing.invited_date_text = invited_date_text;
}
}
const rows = Array.from(byName.values());
return { url: href, title: document.title || '', authRequired, warning, count: rows.length, rows, bodyText: text.slice(0, 1000) };
})()`;
}
cli({
site: 'linkedin',
name: 'sent-invitations',
access: 'read',
description: 'List pending LinkedIn sent invitations for CRM reconciliation',
domain: LINKEDIN_DOMAIN,
strategy: Strategy.UI,
browser: true,
args: [],
columns: ['rank', 'name', 'profile_url', 'invited_date_text'],
func: async (page) => {
if (!page) throw new CommandExecutionError('Browser session required for linkedin sent-invitations');
await page.goto(SENT_URL);
await page.wait(12);
let result = unwrapEvaluateResult(await page.evaluate(buildSentInvitationsScript()));
if (result?.authRequired) {
throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn sent invitations requires an active signed-in browser session.');
}
if (result?.warning) {
throw new CommandExecutionError('LinkedIn warning/restriction state visible on sent invitations page.');
}
const rows = Array.isArray(result?.rows) ? result.rows : [];
return rows.map((row, index) => ({
rank: index + 1,
name: row.name || '',
profile_url: row.profile_url || '',
invited_date_text: row.invited_date_text || '',
}));
},
});
export const __test__ = {
buildSentInvitationsScript,
unwrapEvaluateResult,
};