180 lines
4.5 KiB
JavaScript
180 lines
4.5 KiB
JavaScript
function cleanText(value) {
|
|
return String(value || '')
|
|
.replace(/\s+/g, ' ')
|
|
.replace(/\u200b/g, '')
|
|
.trim();
|
|
}
|
|
|
|
function pageText() {
|
|
const body = document.body;
|
|
return cleanText(body && (body.innerText || body.textContent || ''));
|
|
}
|
|
|
|
function isLoginBlocked(url, text) {
|
|
return /\/signin\b|\/signup\b/.test(url) ||
|
|
/登录|注册|验证码|安全验证|验证后继续|请先登录/.test(text);
|
|
}
|
|
|
|
function isVisible(node) {
|
|
if (!node) {
|
|
return false;
|
|
}
|
|
const rect = typeof node.getBoundingClientRect === 'function' ? node.getBoundingClientRect() : null;
|
|
return !rect || rect.width > 0 || rect.height > 0;
|
|
}
|
|
|
|
function attrText(node, name) {
|
|
if (!node || typeof node.getAttribute !== 'function') {
|
|
return '';
|
|
}
|
|
return cleanText(node.getAttribute(name) || '');
|
|
}
|
|
|
|
function looksLikeTitleInput(node) {
|
|
const signals = [
|
|
attrText(node, 'placeholder'),
|
|
attrText(node, 'data-placeholder'),
|
|
attrText(node, 'aria-label'),
|
|
].filter(Boolean);
|
|
return signals.some((value) => value.includes('标题'));
|
|
}
|
|
|
|
function dispatchTextInput(node) {
|
|
node.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
node.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
}
|
|
|
|
function fillInput(node, value) {
|
|
node.focus();
|
|
if ('value' in node) {
|
|
node.value = '';
|
|
dispatchTextInput(node);
|
|
node.value = value;
|
|
dispatchTextInput(node);
|
|
return;
|
|
}
|
|
|
|
node.textContent = value;
|
|
dispatchTextInput(node);
|
|
}
|
|
|
|
function fillEditable(node, value) {
|
|
node.focus();
|
|
node.innerHTML = '';
|
|
const lines = String(value || '').split(/\n/);
|
|
lines.forEach((line, index) => {
|
|
if (index > 0) {
|
|
node.appendChild(document.createElement('br'));
|
|
}
|
|
node.appendChild(document.createTextNode(line));
|
|
});
|
|
dispatchTextInput(node);
|
|
}
|
|
|
|
function findVisible(selectors) {
|
|
for (const selector of selectors) {
|
|
const nodes = Array.from(document.querySelectorAll(selector));
|
|
const match = nodes.find((node) => {
|
|
return isVisible(node);
|
|
});
|
|
if (match) {
|
|
return match;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function findBodyEditor(titleInput) {
|
|
for (const selector of [
|
|
"div[contenteditable='true'][role='textbox']",
|
|
"div.public-DraftEditor-content[contenteditable='true']",
|
|
"[role='textbox'][contenteditable='true']",
|
|
"[contenteditable='true'][data-placeholder]",
|
|
"div[contenteditable='true']",
|
|
]) {
|
|
const nodes = Array.from(document.querySelectorAll(selector));
|
|
const match = nodes.find((node) => isVisible(node) && node !== titleInput && !looksLikeTitleInput(node));
|
|
if (match) {
|
|
return match;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function findButtonByText(fragment) {
|
|
const candidates = Array.from(document.querySelectorAll("button, [role='button'], a"));
|
|
const wanted = cleanText(fragment);
|
|
return candidates.find((node) => {
|
|
const text = cleanText(node.textContent);
|
|
if (!text || !text.includes(wanted)) {
|
|
return false;
|
|
}
|
|
return isVisible(node);
|
|
}) || null;
|
|
}
|
|
|
|
const currentUrl = location.href;
|
|
const text = pageText();
|
|
if (isLoginBlocked(currentUrl, text)) {
|
|
return {
|
|
status: 'login_required',
|
|
current_url: currentUrl,
|
|
};
|
|
}
|
|
|
|
const titleInput = findVisible([
|
|
"textarea[placeholder*='标题']",
|
|
"input[placeholder*='标题']",
|
|
"textarea[data-placeholder*='标题']",
|
|
"input[data-placeholder*='标题']",
|
|
"[role='textbox'][aria-label*='标题']",
|
|
"[contenteditable='true'][aria-label*='标题']",
|
|
"[contenteditable='true'][data-placeholder*='标题']",
|
|
]);
|
|
const bodyEditor = findBodyEditor(titleInput);
|
|
|
|
if (!titleInput || !bodyEditor) {
|
|
return {
|
|
status: 'editor_not_ready',
|
|
current_url: currentUrl,
|
|
};
|
|
}
|
|
|
|
fillInput(titleInput, String(args.title || ''));
|
|
fillEditable(bodyEditor, String(args.body || ''));
|
|
|
|
const publishMode = String(args.publish_mode || '').toLowerCase() === 'true';
|
|
if (!publishMode) {
|
|
return {
|
|
status: 'draft_ready',
|
|
current_url: currentUrl,
|
|
title: cleanText(args.title),
|
|
};
|
|
}
|
|
|
|
const publishButton = findButtonByText('发布');
|
|
if (!publishButton) {
|
|
return {
|
|
status: 'publish_button_missing',
|
|
current_url: currentUrl,
|
|
title: cleanText(args.title),
|
|
};
|
|
}
|
|
publishButton.click();
|
|
|
|
const confirmButton = findButtonByText('确认发布');
|
|
if (!confirmButton) {
|
|
return {
|
|
status: 'publish_clicked',
|
|
current_url: currentUrl,
|
|
title: cleanText(args.title),
|
|
};
|
|
}
|
|
confirmButton.click();
|
|
|
|
return {
|
|
status: 'publish_submitted',
|
|
current_url: currentUrl,
|
|
title: cleanText(args.title),
|
|
};
|