Size
12.1 KB
Version
1.1.2
Created
Mar 16, 2026
Updated
about 1 month ago
1// ==UserScript==
2// @name Ozon AI Answer Generator
3// @description AI-powered answer generator for Ozon seller questions
4// @version 1.1.2
5// @match https://*.seller.ozon.ru/*
6// @icon https://st.ozone.ru/s3/seller-ui-static/icon/favicon32.png
7// @grant GM.getValue
8// @grant GM.setValue
9// @grant none
10// ==/UserScript==
11(function() {
12 'use strict';
13
14 console.log('Ozon AI Answer Generator initialized');
15
16 // Debounce функция для оптимизации
17 function debounce(func, wait) {
18 let timeout;
19 return function executedFunction(...args) {
20 const later = () => {
21 clearTimeout(timeout);
22 func(...args);
23 };
24 clearTimeout(timeout);
25 timeout = setTimeout(later, wait);
26 };
27 }
28
29 // Функция для добавления стилей
30 function addStyles() {
31 const styles = `
32 .ai-generator-container {
33 margin: 16px 0;
34 padding: 16px;
35 background: #f5f5f7;
36 border-radius: 8px;
37 border: 1px solid #e0e0e0;
38 }
39
40 .ai-generator-title {
41 font-size: 14px;
42 font-weight: 600;
43 margin-bottom: 12px;
44 color: #1a1a1a;
45 }
46
47 .ai-prompt-input {
48 width: 100%;
49 padding: 10px 12px;
50 border: 1px solid #d1d1d6;
51 border-radius: 6px;
52 font-size: 14px;
53 font-family: inherit;
54 resize: vertical;
55 min-height: 60px;
56 margin-bottom: 12px;
57 box-sizing: border-box;
58 }
59
60 .ai-prompt-input:focus {
61 outline: none;
62 border-color: #005bff;
63 box-shadow: 0 0 0 3px rgba(0, 91, 255, 0.1);
64 }
65
66 .ai-generate-btn {
67 background: #005bff;
68 color: white;
69 border: none;
70 padding: 10px 20px;
71 border-radius: 6px;
72 font-size: 14px;
73 font-weight: 500;
74 cursor: pointer;
75 transition: background 0.2s;
76 width: 100%;
77 }
78
79 .ai-generate-btn:hover {
80 background: #0047cc;
81 }
82
83 .ai-generate-btn:disabled {
84 background: #d1d1d6;
85 cursor: not-allowed;
86 }
87
88 .ai-generate-btn.loading {
89 position: relative;
90 color: transparent;
91 }
92
93 .ai-generate-btn.loading::after {
94 content: '';
95 position: absolute;
96 width: 16px;
97 height: 16px;
98 top: 50%;
99 left: 50%;
100 margin-left: -8px;
101 margin-top: -8px;
102 border: 2px solid #ffffff;
103 border-radius: 50%;
104 border-top-color: transparent;
105 animation: spinner 0.6s linear infinite;
106 }
107
108 @keyframes spinner {
109 to { transform: rotate(360deg); }
110 }
111
112 .ai-prompt-hint {
113 font-size: 12px;
114 color: #666;
115 margin-bottom: 8px;
116 }
117 `;
118
119 TM_addStyle(styles);
120 }
121
122 // Функция для создания UI генератора
123 function createGeneratorUI(modal) {
124 // Проверяем, не создан ли уже UI
125 if (modal.querySelector('.ai-generator-container')) {
126 console.log('AI Generator UI already exists');
127 return;
128 }
129
130 // Находим заголовок "Ответ на вопрос"
131 const answerTitleContainer = modal.querySelector('.mt7');
132 if (!answerTitleContainer) {
133 console.error('Answer title container not found in modal');
134 return;
135 }
136
137 // Находим textarea для ответа (для проверки)
138 const textarea = modal.querySelector('textarea');
139 if (!textarea) {
140 console.error('Textarea not found in modal');
141 return;
142 }
143
144 // Создаем контейнер для AI генератора
145 const container = document.createElement('div');
146 container.className = 'ai-generator-container';
147
148 container.innerHTML = `
149 <div class="ai-generator-title">🤖 AI Генератор ответов</div>
150 <div class="ai-prompt-hint">Дополнительные инструкции для AI (необязательно):</div>
151 <textarea class="ai-prompt-input" placeholder="Например: Ответь кратко и дружелюбно, упомяни возрастные ограничения..."></textarea>
152 <button class="ai-generate-btn" data-generated="false">Сгенерировать ответ</button>
153 `;
154
155 // Вставляем контейнер после заголовка "Ответ на вопрос"
156 answerTitleContainer.insertAdjacentElement('afterend', container);
157
158 // Добавляем обработчик на кнопку
159 const generateBtn = container.querySelector('.ai-generate-btn');
160 const promptInput = container.querySelector('.ai-prompt-input');
161
162 generateBtn.addEventListener('click', async () => {
163 await generateAnswer(modal, generateBtn, promptInput, textarea);
164 });
165
166 console.log('AI Generator UI created successfully');
167 }
168
169 // Функция для генерации ответа через AI
170 async function generateAnswer(modal, button, promptInput, textarea) {
171 try {
172 // Отключаем кнопку и показываем загрузку
173 button.disabled = true;
174 button.classList.add('loading');
175
176 // Получаем информацию о продукте
177 const productNameElement = modal.querySelector('.mb1');
178 const productName = productNameElement ? productNameElement.textContent.trim() : 'Неизвестный продукт';
179
180 // Получаем текст вопроса - исправленный селектор
181 const questionLabel = Array.from(modal.querySelectorAll('.c7r110-a.c7r110-b3.body-500'))
182 .find(el => el.textContent.trim() === 'Вопрос');
183 const questionContainer = questionLabel ? questionLabel.closest('.n1d-a1e') : null;
184 const questionTextContainer = questionContainer ? questionContainer.querySelector('.n1d-ae3') : null;
185 const questionElement = questionTextContainer ? questionTextContainer.querySelector('.c7r110-a.c7r110-b2.body-500') : null;
186 const questionText = questionElement ? questionElement.textContent.trim() : '';
187
188 if (!questionText) {
189 console.error('Question not found. Container:', questionContainer);
190 console.error('Text container:', questionTextContainer);
191 console.error('Question element:', questionElement);
192 throw new Error('Не удалось найти текст вопроса');
193 }
194
195 console.log('Product:', productName);
196 console.log('Question:', questionText);
197
198 // Получаем дополнительный промпт
199 const additionalPrompt = promptInput.value.trim();
200 console.log('Additional prompt:', additionalPrompt);
201
202 // Формируем основной промпт
203 let mainPrompt = `Ты - профессиональный менеджер по работе с клиентами на маркетплейсе Ozon.
204
205Продукт: ${productName}
206
207Вопрос покупателя: ${questionText}
208
209Задача: Сгенерируй профессиональный, вежливый и информативный ответ на вопрос покупателя о товаре.
210
211Требования к ответу:
212- Будь вежливым и дружелюбным
213- Отвечай по существу вопроса
214- Используй информацию о продукте
215- Ответ должен быть кратким (2-4 предложения)
216- Не придумывай характеристики, которых нет в названии продукта
217- Если нужна дополнительная информация, вежливо предложи обратиться к описанию товара
218- Учитывай при ответе Дополнительные инструкции ${additionalPrompt} если они есть`;
219
220 // Добавляем дополнительный промпт, если он есть
221 if (additionalPrompt) {
222 mainPrompt += `\n\nДополнительные инструкции: ${additionalPrompt}`;
223 }
224
225 console.log('Calling AI with prompt...');
226
227 // Вызываем AI
228 const answer = await RM.aiCall(mainPrompt);
229
230 console.log('AI Response:', answer);
231
232 // Вставляем ответ в textarea
233 textarea.value = answer;
234
235 // Генерируем событие input для обновления состояния формы
236 const inputEvent = new Event('input', { bubbles: true });
237 textarea.dispatchEvent(inputEvent);
238
239 // Меняем текст кнопки на "Перегенерировать"
240 button.textContent = 'Перегенерировать ответ';
241 button.setAttribute('data-generated', 'true');
242
243 console.log('Answer generated and inserted successfully');
244
245 } catch (error) {
246 console.error('Error generating answer:', error);
247 alert('Ошибка при генерации ответа: ' + error.message);
248 } finally {
249 // Включаем кнопку обратно
250 button.disabled = false;
251 button.classList.remove('loading');
252 }
253 }
254
255 // Наблюдатель за появлением модального окна
256 function observeModal() {
257 const observer = new MutationObserver(debounce(() => {
258 const modal = document.querySelector('.ct3110-a');
259 if (modal) {
260 // Проверяем, что это модальное окно с вопросом
261 const modalTitle = modal.querySelector('.ct3110-a6.heading-500');
262 if (modalTitle && modalTitle.textContent.includes('Вопрос о товаре')) {
263 console.log('Question modal detected');
264 createGeneratorUI(modal);
265 }
266 }
267 }, 300));
268
269 observer.observe(document.body, {
270 childList: true,
271 subtree: true
272 });
273
274 console.log('Modal observer started');
275 }
276
277 // Инициализация
278 function init() {
279 console.log('Starting Ozon AI Answer Generator...');
280
281 // Добавляем стили
282 addStyles();
283
284 // Проверяем, открыто ли уже модальное окно
285 const existingModal = document.querySelector('.ct3110-a');
286 if (existingModal) {
287 const modalTitle = existingModal.querySelector('.ct3110-a6.heading-500');
288 if (modalTitle && modalTitle.textContent.includes('Вопрос о товаре')) {
289 console.log('Existing question modal found');
290 createGeneratorUI(existingModal);
291 }
292 }
293
294 // Запускаем наблюдатель
295 observeModal();
296 }
297
298 // Запускаем после загрузки DOM
299 if (document.readyState === 'loading') {
300 document.addEventListener('DOMContentLoaded', init);
301 } else {
302 init();
303 }
304
305})();