Ozon AI Answer Generator

AI-powered answer generator for Ozon seller questions

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})();