// ==UserScript==
// @name 阿里巴巴国际站AI产品标题生成器-阉割版
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 在阿里巴巴国际站发品页面生成AI产品标题和关键词组,仅支持DeepSeek大模型
// @author 树洞先生
// @license MIT
// @match https://post.alibaba.com/product/*
// @connect api.deepseek.com
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
// DeepSeek API配置
const DEEPSEEK_CONFIG = {
name: 'DeepSeek',
urls: ['https://api.deepseek.com/v1/chat/completions'],
model: 'deepseek-chat'
};
// 等待页面加载完成
function waitForElement(selector, timeout = 10000) {
return new Promise((resolve, reject) => {
const startTime = Date.now();
function check() {
const element = document.querySelector(selector);
if (element) {
resolve(element);
} else if (Date.now() - startTime > timeout) {
reject(new Error(`Element ${selector} not found within ${timeout}ms`));
} else {
setTimeout(check, 100);
}
}
check();
});
}
// 创建生成按钮
function createGenerateButton() {
const button = document.createElement('button');
button.id = 'ai-title-generator-btn';
button.innerHTML = '🤖 AI生成标题-阉割版-树洞先生';
button.style.cssText = `
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
z-index: 10000;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 12px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
transition: all 0.3s ease;
white-space: nowrap;
`;
// 悬停效果
button.addEventListener('mouseenter', () => {
button.style.transform = 'translateY(-50%) scale(1.05)';
button.style.boxShadow = '0 4px 12px rgba(0,0,0,0.25)';
});
button.addEventListener('mouseleave', () => {
button.style.transform = 'translateY(-50%) scale(1)';
button.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
});
return button;
}
// 定位按钮到商品名称标签右侧
function positionButtonToProductName() {
let tryCount = 0;
const maxTries = 25;
function tryInsert() {
const targetLabel = document.querySelector('label.oly-label-container.left.sell-o-addon-label.required');
if (targetLabel) {
const actionsWrapper = targetLabel.querySelector('.actions-wrapper');
if (actionsWrapper) {
const parent = actionsWrapper.parentElement;
parent.style.position = 'relative';
const existingButton = document.getElementById('ai-title-generator-btn');
if (existingButton) existingButton.remove();
const button = createGenerateButton();
button.style.position = 'absolute';
button.style.right = '0';
button.style.top = '50%';
button.style.transform = 'translateY(-50%)';
button.style.height = actionsWrapper.offsetHeight + 'px';
button.style.lineHeight = actionsWrapper.offsetHeight + 'px';
button.style.fontSize = '14px';
button.style.boxSizing = 'border-box';
button.style.padding = '0 16px';
button.style.margin = '0';
button.style.zIndex = 10;
parent.appendChild(button);
button.addEventListener('click', () => {
const modal = document.getElementById('ai-title-modal');
if (modal) {
modal.style.display = 'flex';
if (typeof loadAPIConfig === 'function') loadAPIConfig();
if (typeof setupEventListeners === 'function') setupEventListeners(modal);
}
});
return true;
}
}
tryCount++;
if (tryCount < maxTries) {
setTimeout(tryInsert, 200);
} else {
return false;
}
return false;
}
return tryInsert();
}
// 创建弹窗
function createModal() {
const modal = document.createElement('div');
modal.id = 'ai-title-modal';
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 10001;
display: none;
justify-content: center;
align-items: center;
`;
const modalContent = document.createElement('div');
modalContent.style.cssText = `
background: white;
padding: 30px;
border-radius: 12px;
width: 600px;
max-width: 90vw;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
position: relative;
`;
modalContent.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px;">
<h2 style="margin: 0; color: #333; font-size: 20px;">AI产品标题生成器-DeepSeek-阉割版</h2>
<button id="close-modal" style="background: none; border: none; font-size: 24px; cursor: pointer; color: #999;">×</button>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #555;">输入关键词(中文/英文):</label>
<input type="text" id="keyword-input" placeholder="请输入产品关键词,如:风扇、连衣裙、充电器..." style="
width: 100%;
padding: 12px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 14px;
box-sizing: border-box;
transition: border-color 0.3s ease;
" />
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #555;">DeepSeek API配置:</label>
<div style="display: flex; gap: 15px; align-items: center; margin-bottom: 10px;">
<select id="ai-model-select" style="
padding: 8px 12px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 14px;
background: white;
min-width: 180px;
">
<option value="deepseek-chat">deepseek-chat</option>
<option value="deepseek-reasoner">deepseek-reasoner</option>
</select>
<input type="text" id="api-key" placeholder="请输入DeepSeek API Key" style="
flex: 1;
padding: 8px 12px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 14px;
box-sizing: border-box;
" />
<button id="test-api-btn" style="
background: #2196f3;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
margin-left: 4px;
transition: all 0.3s ease;
">测试API</button>
<button id="diagnose-btn" style="
background: #ff9800;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
margin-left: 4px;
transition: all 0.3s ease;
">诊断问题</button>
</div>
<div style="font-size: 12px; color: #666; margin-top: 5px;">
💡 使用DeepSeek大模型生成高质量的产品标题和关键词组
</div>
</div>
<!-- 自定义AI提示词输入区 -->
<div id="custom-prompt-area" style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #555;">
自定义AI提示词(可选):
<button id="toggle-prompt-visibility" style="margin-left:10px;font-size:12px;background:#f8f9fa;border:1px solid #ddd;border-radius:3px;padding:2px 6px;cursor:pointer;">隐藏/显示</button>
</label>
<textarea id="custom-prompt-input" placeholder="可自定义AI提示词,留空则用默认提示词" style="
width: 100%;
height: 60px;
padding: 10px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 14px;
resize: vertical;
box-sizing: border-box;
background-color: #f8f9fa;
margin-bottom: 8px;
"></textarea>
<div style="display:flex;gap:8px;align-items:center;">
<button id="save-prompt-template-btn" style="font-size:12px;background:#28a745;color:white;border:none;padding:4px 8px;border-radius:3px;cursor:pointer;">保存为模板</button>
<select id="prompt-template-select" style="flex:1;font-size:12px;padding:4px;border:1px solid #ddd;border-radius:3px;">
<option value="">选择已保存的模板</option>
</select>
<button id="delete-prompt-template-btn" style="font-size:12px;background:#dc3545;color:white;border:none;padding:4px 8px;border-radius:3px;cursor:pointer;">删除模板</button>
</div>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; margin-bottom: 8px;">
<button id="generate-btn" style="
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 6px 14px;
border-radius: 4px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
">🤖 生成</button>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #555;">生成的标题:</label>
<div style="display: flex; align-items: center; gap: 8px;">
<textarea id="generated-title" readonly placeholder="AI生成的英文标题将显示在这里..." style="
width: 100%;
height: 80px;
padding: 12px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 14px;
resize: vertical;
box-sizing: border-box;
background-color: #f8f9fa;
"></textarea>
<button id="copy-title-btn" style="
background: #28a745;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
margin-left: 4px;
transition: all 0.3s ease;
">复制</button>
</div>
<div id="title-char-count" style="text-align: right; font-size: 12px; color: #666; margin-top: 5px;">0/125 字符</div>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #555;">生成的副标题:</label>
<div style="display: flex; align-items: center; gap: 8px;">
<textarea id="generated-subtitle" readonly placeholder="AI生成的英文副标题将显示在这里..." style="
width: 100%;
height: 60px;
padding: 12px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 14px;
resize: vertical;
box-sizing: border-box;
background-color: #f8f9fa;
"></textarea>
<button id="copy-subtitle-btn" style="
background: #ff9800;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
margin-left: 4px;
transition: all 0.3s ease;
">复制</button>
</div>
<div id="subtitle-char-count" style="text-align: right; font-size: 12px; color: #666; margin-top: 5px;">0/125 字符</div>
</div>
<div style="margin-bottom: 25px;">
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: #555;">生成的关键词组:</label>
<div style="display: flex; align-items: center; gap: 8px;">
<textarea id="generated-keywords" readonly placeholder="AI生成的英文关键词组将显示在这里..." style="
width: 100%;
height: 100px;
padding: 12px;
border: 2px solid #e1e5e9;
border-radius: 6px;
font-size: 14px;
resize: vertical;
box-sizing: border-box;
background-color: #f8f9fa;
"></textarea>
<button id="copy-keywords-btn" style="
background: #17a2b8;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 13px;
cursor: pointer;
margin-left: 4px;
transition: all 0.3s ease;
">复制</button>
</div>
<div id="keywords-char-count" style="text-align: right; font-size: 12px; color: #666; margin-top: 5px;">0/350 字符</div>
</div>
`;
modal.appendChild(modalContent);
return modal;
}
// 初始化脚本
async function init() {
try {
await waitForElement('body');
let modal = document.getElementById('ai-title-modal');
if (!modal) {
modal = createModal();
document.body.appendChild(modal);
}
loadAPIConfig();
setTimeout(() => {
let buttonPositioned = positionButtonToProductName();
if (!buttonPositioned) {
const button = createDefaultButton();
document.body.appendChild(button);
button.addEventListener('click', () => {
const modal = document.getElementById('ai-title-modal');
if (modal) {
modal.style.display = 'flex';
if (typeof loadAPIConfig === 'function') loadAPIConfig();
} else {
alert('弹窗未正确加载,请刷新页面重试');
}
});
}
}, 1000);
if (modal) {
const closeBtn = modal.querySelector('#close-modal');
if (closeBtn && !closeBtn.hasAttribute('data-inited')) {
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
});
closeBtn.setAttribute('data-inited', '1');
}
if (!modal.hasAttribute('data-inited')) {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.style.display = 'none';
}
});
modal.setAttribute('data-inited', '1');
}
}
} catch (error) {
console.error('脚本初始化失败:', error);
}
}
// 创建默认定位的按钮(备用方案)
function createDefaultButton() {
const button = document.createElement('button');
button.id = 'ai-title-generator-btn';
button.innerHTML = '🤖 AI生成标题-阉割版';
button.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
transition: all 0.3s ease;
`;
button.addEventListener('mouseenter', () => {
button.style.transform = 'translateY(-2px)';
button.style.boxShadow = '0 6px 20px rgba(0,0,0,0.3)';
});
button.addEventListener('mouseleave', () => {
button.style.transform = 'translateY(0)';
button.style.boxShadow = '0 4px 15px rgba(0,0,0,0.2)';
});
return button;
}
// 设置事件监听器
function setupEventListeners(modal) {
const closeBtn = modal.querySelector('#close-modal');
if (closeBtn && !closeBtn.hasAttribute('data-inited')) {
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
});
closeBtn.setAttribute('data-inited', '1');
}
if (!modal.hasAttribute('data-inited')) {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.style.display = 'none';
}
});
modal.setAttribute('data-inited', '1');
}
const keywordInput = modal.querySelector('#keyword-input');
if (keywordInput && !keywordInput.hasAttribute('data-inited')) {
keywordInput.addEventListener('focus', () => {
keywordInput.style.borderColor = '#667eea';
});
keywordInput.addEventListener('blur', () => {
keywordInput.style.borderColor = '#e1e5e9';
});
keywordInput.setAttribute('data-inited', '1');
}
const apiKeyInput = modal.querySelector('#api-key');
if (apiKeyInput && !apiKeyInput.hasAttribute('data-inited')) {
apiKeyInput.addEventListener('input', () => {
apiKeyInput.setAttribute('data-real-key', apiKeyInput.value);
});
apiKeyInput.addEventListener('blur', () => {
let realKey = apiKeyInput.value;
if (realKey.includes('*')) {
realKey = apiKeyInput.getAttribute('data-real-key') || '';
}
GM_setValue('deepseek_api_key', realKey);
apiKeyInput.value = maskApiKey(realKey);
apiKeyInput.setAttribute('data-real-key', realKey);
});
apiKeyInput.setAttribute('data-inited', '1');
}
if (apiKeyInput && !apiKeyInput.hasAttribute('data-mask-inited')) {
let realApiKey = apiKeyInput.getAttribute('data-real-key') || apiKeyInput.value;
apiKeyInput.value = maskApiKey(realApiKey);
apiKeyInput.setAttribute('data-real-key', realApiKey);
apiKeyInput.setAttribute('data-mask-inited', '1');
}
const titleTextarea = modal.querySelector('#generated-title');
const subtitleTextarea = modal.querySelector('#generated-subtitle');
const keywordsTextarea = modal.querySelector('#generated-keywords');
const titleCharCount = modal.querySelector('#title-char-count');
const subtitleCharCount = modal.querySelector('#subtitle-char-count');
const keywordsCharCount = modal.querySelector('#keywords-char-count');
if (titleTextarea && !titleTextarea.hasAttribute('data-inited')) {
titleTextarea.addEventListener('input', () => {
const count = titleTextarea.value.length;
titleCharCount.textContent = `${count}/125 字符`;
titleCharCount.style.color = count > 125 ? '#dc3545' : '#666';
});
titleTextarea.setAttribute('data-inited', '1');
}
if (subtitleTextarea && !subtitleTextarea.hasAttribute('data-inited')) {
subtitleTextarea.addEventListener('input', () => {
const count = subtitleTextarea.value.length;
subtitleCharCount.textContent = `${count}/125 字符`;
subtitleCharCount.style.color = count > 125 ? '#dc3545' : '#666';
});
subtitleTextarea.setAttribute('data-inited', '1');
}
if (keywordsTextarea && !keywordsTextarea.hasAttribute('data-inited')) {
keywordsTextarea.addEventListener('input', () => {
const count = keywordsTextarea.value.length;
keywordsCharCount.textContent = `${count}/350 字符`;
keywordsCharCount.style.color = count > 350 ? '#dc3545' : '#666';
});
keywordsTextarea.setAttribute('data-inited', '1');
}
const generateBtn = modal.querySelector('#generate-btn');
if (generateBtn && !generateBtn.hasAttribute('data-inited')) {
generateBtn.addEventListener('click', generateContent);
generateBtn.setAttribute('data-inited', '1');
}
const copyTitleBtn = modal.querySelector('#copy-title-btn');
if (copyTitleBtn && !copyTitleBtn.hasAttribute('data-inited')) {
copyTitleBtn.addEventListener('click', () => {
copyToClipboard(titleTextarea.value, '标题已复制到剪贴板');
});
copyTitleBtn.setAttribute('data-inited', '1');
}
const copySubtitleBtn = modal.querySelector('#copy-subtitle-btn');
if (copySubtitleBtn && !copySubtitleBtn.hasAttribute('data-inited')) {
copySubtitleBtn.addEventListener('click', () => {
copyToClipboard(subtitleTextarea.value, '副标题已复制到剪贴板');
});
copySubtitleBtn.setAttribute('data-inited', '1');
}
const copyKeywordsBtn = modal.querySelector('#copy-keywords-btn');
if (copyKeywordsBtn && !copyKeywordsBtn.hasAttribute('data-inited')) {
copyKeywordsBtn.addEventListener('click', () => {
copyToClipboard(keywordsTextarea.value, '关键词已复制到剪贴板');
});
copyKeywordsBtn.setAttribute('data-inited', '1');
}
if (keywordInput && !keywordInput.hasAttribute('data-enter-inited')) {
keywordInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
generateContent();
}
});
keywordInput.setAttribute('data-enter-inited', '1');
}
const testApiBtn = modal.querySelector('#test-api-btn');
if (testApiBtn && !testApiBtn.hasAttribute('data-inited')) {
testApiBtn.addEventListener('click', async () => {
let apiKey = apiKeyInput.getAttribute('data-real-key') || apiKeyInput.value;
if (!apiKey.trim()) {
showNotification('请先输入API Key', 'warning');
return;
}
testApiBtn.disabled = true;
testApiBtn.textContent = '测试中...';
try {
const result = await testDeepSeekAPI(apiKey);
showNotification('API测试成功: ' + result, 'success');
} catch (e) {
showNotification('API测试失败: ' + (e.message || e), 'error');
} finally {
testApiBtn.disabled = false;
testApiBtn.textContent = '测试API';
}
});
testApiBtn.setAttribute('data-inited', '1');
}
// 自定义提示词相关功能
const promptInput = modal.querySelector('#custom-prompt-input');
const savePromptBtn = modal.querySelector('#save-prompt-template-btn');
const promptSelect = modal.querySelector('#prompt-template-select');
const deletePromptBtn = modal.querySelector('#delete-prompt-template-btn');
const togglePromptBtn = modal.querySelector('#toggle-prompt-visibility');
// 加载模板到下拉框
function loadPromptTemplates() {
const templates = GM_getValue('deepseek_prompt_templates', []);
promptSelect.innerHTML = '<option value="">选择已保存的模板</option>';
templates.forEach((tpl, idx) => {
const opt = document.createElement('option');
opt.value = idx;
opt.textContent = tpl.slice(0, 30).replace(/\n/g, ' ') + (tpl.length > 30 ? '...' : '');
promptSelect.appendChild(opt);
});
}
// 选择模板自动填充
if (promptSelect && !promptSelect.hasAttribute('data-inited')) {
promptSelect.addEventListener('change', () => {
const templates = GM_getValue('deepseek_prompt_templates', []);
if (promptSelect.value && templates[promptSelect.value]) {
promptInput.value = templates[promptSelect.value];
}
});
promptSelect.setAttribute('data-inited', '1');
}
// 保存为模板
if (savePromptBtn && !savePromptBtn.hasAttribute('data-inited')) {
savePromptBtn.addEventListener('click', () => {
const val = promptInput.value.trim();
if (!val) {
showNotification('提示词不能为空', 'warning');
return;
}
let templates = GM_getValue('deepseek_prompt_templates', []);
if (!templates.includes(val)) {
templates.push(val);
GM_setValue('deepseek_prompt_templates', templates);
loadPromptTemplates();
showNotification('模板已保存', 'success');
} else {
showNotification('模板已存在', 'info');
}
});
savePromptBtn.setAttribute('data-inited', '1');
}
// 删除模板
if (deletePromptBtn && !deletePromptBtn.hasAttribute('data-inited')) {
deletePromptBtn.addEventListener('click', () => {
let templates = GM_getValue('deepseek_prompt_templates', []);
if (promptSelect.value && templates[promptSelect.value]) {
templates.splice(promptSelect.value, 1);
GM_setValue('deepseek_prompt_templates', templates);
loadPromptTemplates();
showNotification('模板已删除', 'success');
}
});
deletePromptBtn.setAttribute('data-inited', '1');
}
// 隐藏/显示输入框
let promptVisible = true;
if (togglePromptBtn && !togglePromptBtn.hasAttribute('data-inited')) {
togglePromptBtn.addEventListener('click', () => {
promptVisible = !promptVisible;
promptInput.style.display = promptVisible ? '' : 'none';
togglePromptBtn.textContent = promptVisible ? '隐藏/显示' : '显示提示词';
});
togglePromptBtn.setAttribute('data-inited', '1');
}
// 初始化加载模板
loadPromptTemplates();
// 诊断按钮
const diagnoseBtn = modal.querySelector('#diagnose-btn');
if (diagnoseBtn && !diagnoseBtn.hasAttribute('data-inited')) {
diagnoseBtn.addEventListener('click', () => {
diagnoseProblems();
});
diagnoseBtn.setAttribute('data-inited', '1');
}
}
// 保存API配置
function saveAPIConfig() {
const apiKeyInput = document.querySelector('#api-key');
let realKey = apiKeyInput.getAttribute('data-real-key') || apiKeyInput.value;
GM_setValue('deepseek_api_key', realKey);
}
// 加载API配置
function loadAPIConfig() {
const apiKeyInput = document.querySelector('#api-key');
if (apiKeyInput) {
let realKey = GM_getValue('deepseek_api_key', '');
apiKeyInput.value = maskApiKey(realKey);
apiKeyInput.setAttribute('data-real-key', realKey);
}
}
// 复制到剪贴板
function copyToClipboard(text, message) {
if (!text.trim()) {
showNotification('没有内容可复制', 'warning');
return;
}
navigator.clipboard.writeText(text).then(() => {
showNotification(message, 'success');
}).catch(() => {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showNotification(message, 'success');
});
}
// 显示通知
function showNotification(message, type = 'info') {
let container = document.getElementById('ai-notification-container');
if (!container) {
container = document.createElement('div');
container.id = 'ai-notification-container';
container.style.cssText = `
position: fixed;
top: 80px;
right: 20px;
z-index: 10002;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
pointer-events: none;
`;
document.body.appendChild(container);
}
const notification = document.createElement('div');
notification.style.cssText = `
padding: 12px 20px;
border-radius: 6px;
color: white;
font-size: 14px;
font-weight: bold;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
transition: all 0.3s ease;
margin-bottom: 0;
pointer-events: auto;
`;
switch (type) {
case 'success':
notification.style.background = '#28a745';
break;
case 'warning':
notification.style.background = '#ffc107';
notification.style.color = '#212529';
break;
case 'error':
notification.style.background = '#dc3545';
break;
default:
notification.style.background = '#17a2b8';
}
notification.textContent = message;
container.appendChild(notification);
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
}
// 使用DeepSeek API生成内容
async function generateWithDeepSeek(keyword, customPrompt = '') {
const config = DEEPSEEK_CONFIG;
const aiModelSelect = document.querySelector('#ai-model-select');
let model = aiModelSelect ? aiModelSelect.value : config.model;
// 默认提示词
const defaultPrompt = `请为以下产品关键词生成英文产品标题、副标题和关键词组,严格用于阿里巴巴国际站2025年商品发布,需完全符合平台最新规范:
你是一位经验丰富的阿里巴巴国际站运营专家和SEO优化师。你的任务是为我的一款产品生成高质量、SEO友好且吸引海外买家的产品标题。
请遵循以下规则:
1. 标题必须包含核心关键词,并尽量靠前。
2. 合理组合产品的属性、用途、材质和营销词。
3. 标题总长度不超过125个字符。
4. 语言风格专业,符合B2B采购商的搜索习惯。
5. 标题需要有变化,不要只是简单地堆砌词语。
生成的关键词。请为我提供:
1. 10个核心关键词 (Core Keywords)。
2. 15个长尾关键词 (Long-tail Keywords),包含材质、特性或用途。
3. 5个B2B采购商可能会用的搜索词 (B2B Buyer Search Terms)。
产品关键词: ${keyword}
一、产品标题要求
核心要素:必须包含产品核心关键词(置于标题中前半段),搭配重要属性词(如材质、规格、特性)、同义 / 变体词(如同义词、功能变体)、场景词(如用途、适用领域),结构优先为 “重要属性词 + 核心关键词 + 同义 / 变体词 + 场景词”;
字符限制:总字符数严格控制在 100-125 字符之间,需充分利用字符长度,避免冗余;
介词位置:介词 “for”“with” 需置于标题中后段,禁止出现在前 1/3 位置;
禁止内容:绝对禁止出现任何品牌词(如具体品牌名、自有品牌标识),禁止冗余词汇、联系方式、特殊字符(如 &*#)、关键词堆砌,禁止与后续生成的图片 / 属性描述冲突;
格式规范:每个单词首字母大写(介词、连词、冠词除外),语言专业严谨,符合 B2B 采购商阅读习惯,无口语化表达。
二、产品副标题要求
核心关联:必须包含产品核心关键词,与主标题上下文一致,避免重复主标题及产品属性内容;
内容补充:聚焦主标题未覆盖的卖点(如耐用性、易用性、售后优势)、拓展适用场景(如细分行业用途)或功能细节(如维护方式);
字符与合规:总字符数严格控制在 100-125 字符之间,禁止夸大宣传(如 “best”“top 1”)、虚假信息及违规词汇;
语言风格:延续专业 B2B 调性,用不同表述补充价值,提升买家采购意愿。
三、关键词组要求
数量与内容:包含 5个英文核心关键词,5个长尾关键词,3个B2B采购商可能会用的搜索词,紧密围绕产品核心词、属性词(材质 / 特性)、场景词(用途 / 领域),均为 B2B 买家常用搜索词汇;
格式规范:关键词之间禁止用英文逗号分隔,直接使用空格进行分隔,300 字符≤总字符数≤350 字符;
禁止内容:无无关词汇、无重复词汇、无低俗信息及联系方式,确保每个关键词均具备实际搜索价值。
四、输出格式
严格按照以下格式输出,无任何额外解释、分析或补充内容:
标题: [符合要求的英文产品标题]
副标题: [符合要求的英文产品副标题]
关键词: [符合要求的英文关键词组]
不要输出其它内容。`;
// 使用自定义提示词或默认提示词
let prompt = customPrompt.trim() ? customPrompt.replace('{keyword}', keyword) : defaultPrompt;
// 注入多样化令牌(不应出现在输出中)
const variantHint = Math.random().toString(36).slice(2, 10);
prompt += `\n\n多样化要求:请基于内部变体令牌 ${variantHint} 选择不同的用词与表达方式,避免与同类标题/副标题完全一致。不要在输出中出现该令牌或任何与之相关的内容。`;
const requestData = {
model: model,
messages: [
{
role: "user",
content: prompt
}
],
max_tokens: 500,
temperature: 0.85,
top_p: 0.9,
stream: false
};
const errors = [];
for (let i = 0; i < config.urls.length; i++) {
const url = config.urls[i];
console.log(`尝试API地址 ${i + 1}/${config.urls.length}: ${url}`);
try {
const result = await makeDeepSeekRequest(url, requestData);
return result;
} catch (error) {
errors.push(`地址${i + 1}: ${error.message}`);
if (i < config.urls.length - 1) {
console.log('尝试下一个API地址...');
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
const errorMessage = `所有API地址都请求失败:\n${errors.join('\n')}`;
console.error(errorMessage);
throw new Error(errorMessage);
}
// 发送DeepSeek API请求
function makeDeepSeekRequest(url, requestData) {
return new Promise((resolve, reject) => {
const apiKey = document.querySelector('#api-key').getAttribute('data-real-key') || document.querySelector('#api-key').value;
let headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
};
console.log('发送DeepSeek API请求:', {
url: url,
method: 'POST',
headers: headers,
dataSize: JSON.stringify(requestData).length,
apiKeyLength: apiKey ? apiKey.length : 0
});
console.log('请求数据:', JSON.stringify(requestData, null, 2));
GM_xmlhttpRequest({
method: 'POST',
url: url,
headers: headers,
data: JSON.stringify(requestData),
timeout: 60000, // 增加到60秒
onload: function(response) {
console.log('DeepSeek响应状态:', response.status);
console.log('DeepSeek响应头:', response.responseHeaders);
console.log('DeepSeek原始返回:', response.responseText);
if (response.status === 0) {
reject(new Error('请求被阻止或网络错误 (状态码: 0)'));
return;
}
try {
const data = JSON.parse(response.responseText);
if (response.status === 200 && data.choices && data.choices[0]) {
let content = data.choices[0].message.content;
if ((!content || content.trim() === '') && data.choices[0].message.reasoning_content) {
content = data.choices[0].message.reasoning_content;
}
console.log('AI返回的内容:', content);
const result = parseAIResponse(content);
if (result) {
console.log('解析结果:', result);
resolve(result);
} else {
reject(new Error('AI返回的内容格式不正确'));
}
} else {
console.error('DeepSeek API错误返回:', data);
let errorMessage = '未知错误';
if (data.error && data.error.message) {
errorMessage = data.error.message;
} else if (data.message) {
errorMessage = data.message;
} else if (data.error) {
errorMessage = JSON.stringify(data.error);
}
reject(new Error(`API错误 (${response.status}): ${errorMessage}`));
}
} catch (error) {
console.error('解析响应失败:', error);
console.error('原始响应:', response.responseText);
reject(new Error('解析AI响应失败: ' + error.message));
}
},
onerror: function(error) {
console.error('网络请求错误:', error);
reject(new Error('网络请求失败: ' + (error.error || '未知错误')));
},
ontimeout: function() {
console.error('请求超时');
reject(new Error('请求超时 (60秒)'));
},
onabort: function() {
console.error('请求被中止');
reject(new Error('请求被中止'));
}
});
});
}
// 解析AI API返回的内容
function parseAIResponse(content) {
function normalizeTitleCase(str) {
if (!str) return '';
const lowerCaseWords = [
'a', 'an', 'the',
'and', 'but', 'or', 'nor', 'for', 'so', 'yet',
'at', 'by', 'for', 'in', 'of', 'on', 'to', 'up', 'with', 'as', 'from', 'into', 'like', 'near', 'off', 'onto', 'over', 'per', 'plus', 'than', 'till', 'upon', 'via', 'down', 'out', 'about', 'after', 'before', 'behind', 'below', 'beneath', 'beside', 'between', 'beyond', 'during', 'except', 'inside', 'outside', 'since', 'through', 'under', 'within', 'without', 'over', 'under', 'against', 'along', 'among', 'around', 'because', 'although', 'if', 'unless', 'until', 'while', 'where', 'when', 'once', 'since', 'though', 'even', 'whereas'
];
const words = str.split(/\s+/);
return words.map((word, idx) => {
const w = word.toLowerCase();
if (idx === 0 || idx === words.length - 1) {
return word.charAt(0).toUpperCase() + word.slice(1);
}
if (lowerCaseWords.includes(w)) {
return w;
}
return word.charAt(0).toUpperCase() + word.slice(1);
}).join(' ');
}
function cleanKeywords(str) {
if (!str) return '';
let s = str.replace(/[,,.;:!!。??、\-_=+~`@#$%^&*()\[\]{}|\\/<>'\"""""'' ]/g, ' ');
s = s.replace(/[^a-zA-Z0-9 ]/g, '');
s = s.replace(/\s+/g, ' ');
s = s.trim();
return s;
}
if (!content || content.trim() === '') {
return null;
}
const titleMatch = content.match(/(?:\*\*|__)?标题(?:\*\*|__)?[:::\s]*([^\n]+?)(?=\n|副标题|关键词|$)/i);
const subtitleMatch = content.match(/(?:\*\*|__)?副标题(?:\*\*|__)?[:::\s]*([^\n]+?)(?=\n|关键词|$)/i);
const keywordsMatch = content.match(/(?:\*\*|__)?关键词(?:\*\*|__)?[:::\s]*([^\n]+?)(?=\n|$)/i);
if (titleMatch && keywordsMatch) {
let title = titleMatch[1].trim();
let subtitle = subtitleMatch ? subtitleMatch[1].trim() : '';
let keywords = keywordsMatch[1].trim();
title = title.replace(/^[*]{2,}/, '').replace(/^[\[\]【】]/g, '').trim();
subtitle = subtitle.replace(/^[*]{2,}/, '').replace(/[\[\]【】]/g, '').trim();
keywords = keywords.replace(/[\[\]【】]/g, '').trim();
title = title.replace(/[\u4e00-\u9fa5]/g, '').replace(/\s+/g, ' ').trim();
subtitle = subtitle.replace(/[\u4e00-\u9fa5]/g, '').replace(/\s+/g, ' ').trim();
keywords = keywords.replace(/[\u4e00-\u9fa5]/g, '').replace(/\s+/g, ' ').trim();
title = normalizeTitleCase(title);
subtitle = normalizeTitleCase(subtitle);
keywords = cleanKeywords(keywords);
if (title.length > 128) {
title = title.substring(0, 128).trim();
const lastSpaceIndex = title.lastIndexOf(' ');
if (lastSpaceIndex > 100) {
title = title.substring(0, lastSpaceIndex);
}
}
if (subtitle.length > 128) {
subtitle = subtitle.substring(0, 128).trim();
const lastSpaceIndex = subtitle.lastIndexOf(' ');
if (lastSpaceIndex > 100) {
subtitle = subtitle.substring(0, lastSpaceIndex);
}
}
if (keywords.length > 350) {
keywords = keywords.substring(0, 350).trim();
const lastCommaIndex = keywords.lastIndexOf(',');
if (lastCommaIndex > 300) {
keywords = keywords.substring(0, lastCommaIndex);
}
}
return { title, subtitle, keywords };
}
return null;
}
// 生成内容
async function generateContent() {
const keywordInput = document.querySelector('#keyword-input');
const titleTextarea = document.querySelector('#generated-title');
const subtitleTextarea = document.querySelector('#generated-subtitle');
const keywordsTextarea = document.querySelector('#generated-keywords');
const generateBtn = document.querySelector('#generate-btn');
const apiKey = document.querySelector('#api-key').getAttribute('data-real-key') || document.querySelector('#api-key').value;
const keyword = keywordInput.value.trim();
const customPrompt = document.getElementById('custom-prompt-input')?.value?.trim() || '';
if (!keyword) {
showNotification('请输入关键词', 'warning');
return;
}
if (!apiKey.trim()) {
showNotification('请先配置API Key', 'warning');
return;
}
generateBtn.disabled = true;
const originalText = generateBtn.textContent;
generateBtn.textContent = '🔄 DeepSeek生成中...';
showNotification('正在使用DeepSeek AI生成...', 'info');
try {
const result = await generateWithDeepSeek(keyword, customPrompt);
titleTextarea.value = result.title;
if (subtitleTextarea) subtitleTextarea.value = result.subtitle || '';
keywordsTextarea.value = result.keywords;
titleTextarea.dispatchEvent(new Event('input'));
if (subtitleTextarea) subtitleTextarea.dispatchEvent(new Event('input'));
keywordsTextarea.dispatchEvent(new Event('input'));
showNotification('DeepSeek AI生成完成!', 'success');
} catch (error) {
let errorMessage = '生成失败,请重试';
if (error.message.includes('API错误')) {
errorMessage = 'DeepSeek API调用失败,请检查API Key和网络连接';
} else if (error.message.includes('网络请求失败')) {
errorMessage = '网络连接失败,请检查网络设置';
}
showNotification(errorMessage, 'error');
} finally {
generateBtn.disabled = false;
generateBtn.textContent = originalText;
const apiKeyInput = document.querySelector('#api-key');
if (apiKeyInput) {
apiKeyInput.blur();
}
}
}
// API密钥打码函数
function maskApiKey(apiKey) {
if (!apiKey) return '';
if (apiKey.length <= 8) return apiKey[0] + '****' + apiKey[apiKey.length - 1];
return apiKey.slice(0, 4) + '****' + apiKey.slice(-4);
}
// 诊断问题
function diagnoseProblems() {
const apiKey = document.querySelector('#api-key').getAttribute('data-real-key') || document.querySelector('#api-key').value;
const model = document.querySelector('#ai-model-select').value;
let issues = [];
let suggestions = [];
// 检查API Key
if (!apiKey || apiKey.trim() === '') {
issues.push('❌ API Key为空');
suggestions.push('请输入有效的DeepSeek API Key');
} else if (apiKey.length < 20) {
issues.push('❌ API Key长度异常');
suggestions.push('请检查API Key是否正确');
} else {
issues.push('✅ API Key已配置');
}
// 检查模型选择
if (model) {
issues.push(`✅ 模型选择: ${model}`);
} else {
issues.push('❌ 未选择模型');
suggestions.push('请选择DeepSeek模型');
}
// 检查网络连接
issues.push('🔍 正在检查网络连接...');
// 测试网络连接
GM_xmlhttpRequest({
method: 'GET',
url: 'https://api.deepseek.com/v1/models',
headers: {
'Authorization': `Bearer ${apiKey}`,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
},
timeout: 10000,
onload: function(response) {
if (response.status === 200) {
issues.push('✅ 网络连接正常');
} else {
issues.push(`❌ 网络连接异常 (状态码: ${response.status})`);
suggestions.push('请检查网络连接或API Key');
}
showDiagnosticResult(issues, suggestions);
},
onerror: function() {
issues.push('❌ 无法连接到DeepSeek API');
suggestions.push('请检查网络连接、防火墙设置或使用代理');
showDiagnosticResult(issues, suggestions);
},
ontimeout: function() {
issues.push('❌ 网络连接超时');
suggestions.push('请检查网络连接或尝试使用代理');
showDiagnosticResult(issues, suggestions);
}
});
}
// 显示诊断结果
function showDiagnosticResult(issues, suggestions) {
let message = '🔍 诊断结果:\n\n' + issues.join('\n');
if (suggestions.length > 0) {
message += '\n\n💡 建议:\n' + suggestions.join('\n');
}
// 创建诊断结果弹窗
const diagnosticModal = document.createElement('div');
diagnosticModal.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
z-index: 10003;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
`;
diagnosticModal.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
<h3 style="margin: 0; color: #333;">诊断结果</h3>
<button onclick="this.parentElement.parentElement.remove()" style="background: none; border: none; font-size: 20px; cursor: pointer; color: #999;">×</button>
</div>
<div style="white-space: pre-line; font-family: monospace; font-size: 12px; line-height: 1.5;">${message}</div>
<div style="margin-top: 15px; text-align: center;">
<button onclick="this.parentElement.parentElement.remove()" style="background: #2196f3; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;">关闭</button>
</div>
`;
document.body.appendChild(diagnosticModal);
}
// 测试DeepSeek API连接
async function testDeepSeekAPI(apiKey) {
const requestData = {
model: 'deepseek-chat',
messages: [
{ role: 'user', content: 'Say hello world.' }
],
max_tokens: 10,
temperature: 0.1,
stream: false
};
return new Promise((resolve, reject) => {
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
};
console.log('测试API请求:', {
url: DEEPSEEK_CONFIG.urls[0],
headers: headers,
data: requestData
});
GM_xmlhttpRequest({
method: 'POST',
url: DEEPSEEK_CONFIG.urls[0],
headers: headers,
data: JSON.stringify(requestData),
timeout: 30000,
onload: function(response) {
console.log('测试API响应状态:', response.status);
console.log('测试API响应:', response.responseText);
if (response.status === 0) {
reject(new Error('请求被阻止或网络错误 (状态码: 0)'));
return;
}
try {
const data = JSON.parse(response.responseText);
if (response.status === 200 && data.choices && data.choices[0]) {
const content = data.choices[0].message.content;
resolve(content);
} else {
let errorMessage = '未知错误';
if (data.error && data.error.message) errorMessage = data.error.message;
else if (data.message) errorMessage = data.message;
else if (data.error) errorMessage = JSON.stringify(data.error);
reject(new Error(`API错误 (${response.status}): ${errorMessage}`));
}
} catch (e) {
console.error('测试API解析失败:', e);
reject(new Error('解析响应失败: ' + e.message));
}
},
onerror: function(e) {
console.error('测试API网络错误:', e);
reject(new Error('网络请求失败: ' + (e.error || '未知错误')));
},
ontimeout: function() {
console.error('测试API超时');
reject(new Error('请求超时 (30秒)'));
},
onabort: function() {
console.error('测试API被中止');
reject(new Error('请求被中止'));
}
});
});
}
// 启动脚本
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();