Wildberries Description Generator

Генератор SEO-описаний для товаров на Wildberries с анализом ключевых слов

Size

39.2 KB

Version

2.3.1

Created

Jan 12, 2026

Updated

about 1 month ago

1// ==UserScript==
2// @name		Wildberries Description Generator
3// @description		Генератор SEO-описаний для товаров на Wildberries с анализом ключевых слов
4// @version		2.3.1
5// @match		https://*.seller.wildberries.ru/*
6// @icon		https://seller.wildberries.ru/favicon.ico
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('Wildberries Description Generator: Расширение запущено');
12
13    // Добавляем стили для модального окна
14    TM_addStyle(`
15        .wb-desc-modal {
16            position: fixed;
17            top: 0;
18            left: 0;
19            width: 100%;
20            height: 100%;
21            background: rgba(0, 0, 0, 0.5);
22            display: flex;
23            align-items: center;
24            justify-content: center;
25            z-index: 10000;
26        }
27        
28        .wb-desc-modal-content {
29            background: white;
30            border-radius: 12px;
31            padding: 24px;
32            max-width: 600px;
33            width: 90%;
34            max-height: 80vh;
35            overflow-y: auto;
36            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
37        }
38        
39        .wb-desc-modal-header {
40            font-size: 20px;
41            font-weight: 600;
42            margin-bottom: 20px;
43            color: #001a34;
44        }
45        
46        .wb-desc-input-group {
47            margin-bottom: 16px;
48        }
49        
50        .wb-desc-label {
51            display: block;
52            margin-bottom: 8px;
53            font-weight: 500;
54            color: #001a34;
55        }
56        
57        .wb-desc-textarea {
58            width: 100%;
59            min-height: 100px;
60            padding: 12px;
61            border: 1px solid #d1d5db;
62            border-radius: 8px;
63            font-size: 14px;
64            font-family: inherit;
65            resize: vertical;
66        }
67        
68        .wb-desc-textarea:focus {
69            outline: none;
70            border-color: #9333ea;
71        }
72        
73        .wb-desc-result {
74            background: #f3f4f6;
75            padding: 16px;
76            border-radius: 8px;
77            margin-bottom: 16px;
78            max-height: 300px;
79            overflow-y: auto;
80            white-space: pre-wrap;
81            word-wrap: break-word;
82        }
83        
84        .wb-desc-char-count {
85            text-align: right;
86            font-size: 12px;
87            color: #6b7280;
88            margin-top: 4px;
89        }
90        
91        .wb-desc-char-count.warning {
92            color: #f59e0b;
93        }
94        
95        .wb-desc-char-count.error {
96            color: #ef4444;
97        }
98        
99        .wb-desc-buttons {
100            display: flex;
101            gap: 12px;
102            justify-content: flex-end;
103            margin-top: 20px;
104        }
105        
106        .wb-desc-btn {
107            padding: 10px 20px;
108            border: none;
109            border-radius: 8px;
110            font-size: 14px;
111            font-weight: 500;
112            cursor: pointer;
113            transition: all 0.2s;
114        }
115        
116        .wb-desc-btn-primary {
117            background: #9333ea;
118            color: white;
119        }
120        
121        .wb-desc-btn-primary:hover {
122            background: #7e22ce;
123        }
124        
125        .wb-desc-btn-primary:disabled {
126            background: #9ca3af;
127            cursor: not-allowed;
128        }
129        
130        .wb-desc-btn-secondary {
131            background: #e5e7eb;
132            color: #374151;
133        }
134        
135        .wb-desc-btn-secondary:hover {
136            background: #d1d5db;
137        }
138        
139        .wb-desc-btn-success {
140            background: #10b981;
141            color: white;
142        }
143        
144        .wb-desc-btn-success:hover {
145            background: #059669;
146        }
147        
148        .wb-desc-generator-btn {
149            margin-left: 12px;
150            padding: 10px 20px;
151            background: #9333ea;
152            color: white;
153            border: none;
154            border-radius: 8px;
155            font-size: 14px;
156            font-weight: 500;
157            cursor: pointer;
158            transition: all 0.2s;
159        }
160        
161        .wb-desc-generator-btn:hover {
162            background: #7e22ce;
163        }
164        
165        .wb-desc-status {
166            margin-top: 12px;
167            padding: 8px 12px;
168            border-radius: 6px;
169            font-size: 13px;
170        }
171        
172        .wb-desc-status.info {
173            background: #dbeafe;
174            color: #1e40af;
175        }
176        
177        .wb-desc-status.success {
178            background: #d1fae5;
179            color: #065f46;
180        }
181        
182        .wb-desc-status.error {
183            background: #fee2e2;
184            color: #991b1b;
185        }
186    `);
187
188    // Функция для создания кнопки генератора
189    function createGeneratorButton() {
190        const descriptionHeader = document.querySelector('.Description-header__zK-9sKs8RX');
191        
192        if (!descriptionHeader) {
193            console.log('Wildberries Description Generator: Заголовок описания не найден');
194            return;
195        }
196        
197        // Проверяем, не добавлена ли уже кнопка
198        if (document.querySelector('.wb-desc-generator-btn')) {
199            console.log('Wildberries Description Generator: Кнопка уже добавлена');
200            return;
201        }
202        
203        const button = document.createElement('button');
204        button.className = 'wb-desc-generator-btn';
205        button.textContent = 'Генератор описаний';
206        button.type = 'button';
207        button.addEventListener('click', openModal);
208        
209        descriptionHeader.appendChild(button);
210        console.log('Wildberries Description Generator: Кнопка добавлена');
211    }
212
213    // Функция для открытия модального окна
214    function openModal() {
215        console.log('Wildberries Description Generator: Открытие модального окна');
216        
217        const modal = document.createElement('div');
218        modal.className = 'wb-desc-modal';
219        modal.innerHTML = `
220            <div class="wb-desc-modal-content">
221                <div class="wb-desc-modal-header">Генератор описаний для Wildberries</div>
222                
223                <div class="wb-desc-input-group">
224                    <label class="wb-desc-label">Введите ключевые слова (каждое с новой строки):</label>
225                    <textarea class="wb-desc-textarea" id="wb-keywords-input" placeholder="Например:&#10;витамины&#10;иммунитет&#10;здоровье"></textarea>
226                </div>
227                
228                <div class="wb-desc-input-group">
229                    <label class="wb-desc-label">Минус-слова (каждое с новой строки):</label>
230                    <textarea class="wb-desc-textarea" style="min-height: 80px;" id="wb-minus-words-input" placeholder="Например:&#10;mixit&#10;nivea&#10;магнит"></textarea>
231                </div>
232                
233                <div id="wb-desc-result-container" style="display: none;">
234                    <div class="wb-desc-label">Сгенерированное описание:</div>
235                    <div class="wb-desc-result" id="wb-desc-result"></div>
236                    <div class="wb-desc-char-count" id="wb-char-count"></div>
237                </div>
238                
239                <div id="wb-desc-status-container"></div>
240                
241                <div class="wb-desc-buttons">
242                    <button class="wb-desc-btn wb-desc-btn-secondary" id="wb-close-btn">Закрыть</button>
243                    <button class="wb-desc-btn wb-desc-btn-primary" id="wb-generate-btn">Сгенерировать</button>
244                    <button class="wb-desc-btn wb-desc-btn-primary" id="wb-regenerate-btn" style="display: none;">Перегенерировать</button>
245                    <button class="wb-desc-btn wb-desc-btn-success" id="wb-insert-btn" style="display: none;">Вставить в описание</button>
246                </div>
247            </div>
248        `;
249        
250        document.body.appendChild(modal);
251        
252        // Обработчики событий
253        modal.addEventListener('click', (e) => {
254            if (e.target === modal) {
255                modal.remove();
256            }
257        });
258        
259        document.getElementById('wb-close-btn').addEventListener('click', () => {
260            modal.remove();
261        });
262        
263        document.getElementById('wb-generate-btn').addEventListener('click', () => {
264            generateDescription(modal);
265        });
266        
267        document.getElementById('wb-regenerate-btn').addEventListener('click', () => {
268            generateDescription(modal);
269        });
270        
271        document.getElementById('wb-insert-btn').addEventListener('click', () => {
272            insertDescription(modal);
273        });
274    }
275
276    // Функция для сбора данных с аналитики
277    async function collectAnalyticsData(keywords, minusWords) {
278        console.log('Wildberries Description Generator: Начало сбора данных с аналитики');
279        
280        // Сохраняем данные для доступа из другой вкладки
281        await GM.setValue('wb_keywords_to_process', JSON.stringify(keywords));
282        await GM.setValue('wb_minus_words', JSON.stringify(minusWords));
283        await GM.setValue('wb_analytics_data', JSON.stringify([]));
284        await GM.setValue('wb_collection_status', 'pending');
285        
286        // Открываем страницу аналитики в новой вкладке
287        const analyticsUrl = 'https://seller.wildberries.ru/search-analytics/popular-search-queries';
288        await GM.openInTab(analyticsUrl, false);
289        
290        console.log('Wildberries Description Generator: Открыта страница аналитики, ожидание сбора данных...');
291        
292        // Ждем завершения сбора данных (максимум 5 минут)
293        const maxWaitTime = 300000; // 5 минут
294        const checkInterval = 2000; // проверяем каждые 2 секунды
295        let waitedTime = 0;
296        
297        while (waitedTime < maxWaitTime) {
298            await new Promise(resolve => setTimeout(resolve, checkInterval));
299            waitedTime += checkInterval;
300            
301            const status = await GM.getValue('wb_collection_status', 'pending');
302            
303            if (status === 'completed') {
304                const analyticsDataStr = await GM.getValue('wb_analytics_data', '[]');
305                const analyticsData = JSON.parse(analyticsDataStr);
306                console.log('Wildberries Description Generator: Данные успешно собраны');
307                return analyticsData;
308            } else if (status === 'error') {
309                console.error('Wildberries Description Generator: Ошибка при сборе данных');
310                return [];
311            }
312        }
313        
314        console.error('Wildberries Description Generator: Превышено время ожидания сбора данных');
315        return [];
316    }
317
318    // Функция для автоматического сбора данных на странице аналитики
319    async function autoCollectOnAnalyticsPage() {
320        // Проверяем, что мы на странице аналитики
321        if (!window.location.href.includes('seller.wildberries.ru/search-analytics/popular-search-queries')) {
322            return;
323        }
324        
325        console.log('Wildberries Description Generator: Обнаружена страница аналитики');
326        
327        // Проверяем, есть ли задача на сбор данных
328        const status = await GM.getValue('wb_collection_status', 'none');
329        if (status !== 'pending') {
330            return;
331        }
332        
333        console.log('Wildberries Description Generator: Начинаем автоматический сбор данных');
334        
335        try {
336            const keywordsStr = await GM.getValue('wb_keywords_to_process', '[]');
337            const minusWordsStr = await GM.getValue('wb_minus_words', '[]');
338            const keywords = JSON.parse(keywordsStr);
339            const minusWords = JSON.parse(minusWordsStr);
340            
341            const analyticsData = [];
342            
343            // Ждем загрузки страницы
344            await new Promise(resolve => setTimeout(resolve, 3000));
345            
346            for (const keyword of keywords) {
347                console.log(`Wildberries Description Generator: Обработка ключевого слова: ${keyword}`);
348                
349                try {
350                    // Находим поле поиска
351                    const searchInput = document.querySelector('input[name="searchString"]');
352                    if (!searchInput) {
353                        console.error('Wildberries Description Generator: Поле поиска не найдено');
354                        continue;
355                    }
356                    
357                    // Очищаем и вводим ключевое слово
358                    searchInput.value = '';
359                    searchInput.focus();
360                    searchInput.value = keyword;
361                    searchInput.dispatchEvent(new Event('input', { bubbles: true }));
362                    searchInput.dispatchEvent(new Event('change', { bubbles: true }));
363                    
364                    // Ждем загрузки результатов
365                    await new Promise(resolve => setTimeout(resolve, 5000));
366                    
367                    // Собираем данные из таблицы
368                    const rows = document.querySelectorAll('table tbody tr');
369                    const keywordData = {
370                        keyword: keyword,
371                        queries: []
372                    };
373                    
374                    console.log(`Wildberries Description Generator: Найдено строк в таблице: ${rows.length}`);
375                    
376                    rows.forEach(row => {
377                        const cells = row.querySelectorAll('td');
378                        if (cells.length >= 2) {
379                            const query = cells[0]?.textContent?.trim();
380                            const popularity = cells[1]?.textContent?.trim();
381                            
382                            if (query && popularity) {
383                                // Фильтруем по минус-словам
384                                const queryLower = query.toLowerCase();
385                                const hasMinusWord = minusWords.some(minusWord => 
386                                    queryLower.includes(minusWord.toLowerCase())
387                                );
388                                
389                                if (!hasMinusWord) {
390                                    keywordData.queries.push({
391                                        query,
392                                        popularity
393                                    });
394                                } else {
395                                    console.log(`Wildberries Description Generator: Исключен запрос "${query}" (содержит минус-слово)`);
396                                }
397                            }
398                        }
399                    });
400                    
401                    analyticsData.push(keywordData);
402                    console.log(`Wildberries Description Generator: Собрано ${keywordData.queries.length} запросов для "${keyword}"`);
403                    
404                } catch (error) {
405                    console.error(`Wildberries Description Generator: Ошибка при обработке ключевого слова "${keyword}":`, error);
406                }
407            }
408            
409            // Сохраняем собранные данные
410            await GM.setValue('wb_analytics_data', JSON.stringify(analyticsData));
411            await GM.setValue('wb_collection_status', 'completed');
412            
413            console.log('Wildberries Description Generator: Сбор данных завершен, можно закрыть вкладку');
414            
415            // Закрываем вкладку через 2 секунды
416            setTimeout(() => {
417                window.close();
418            }, 2000);
419            
420        } catch (error) {
421            console.error('Wildberries Description Generator: Ошибка при автоматическом сборе данных:', error);
422            await GM.setValue('wb_collection_status', 'error');
423        }
424    }
425
426    // Функция для получения информации о товаре со страницы
427    function getProductInfo() {
428        console.log('Wildberries Description Generator: Сбор информации о товаре');
429        
430        const productInfo = {
431            title: '',
432            currentDescription: '',
433            composition: '',
434            attributes: []
435        };
436        
437        // Получаем текущее описание
438        const descriptionTextarea = document.querySelector('textarea[data-testid="card-form-main-field-description"]');
439        if (descriptionTextarea) {
440            productInfo.currentDescription = descriptionTextarea.value || '';
441        }
442        
443        // Получаем название товара
444        const titleInput = document.querySelector('input[name*="name"], input[placeholder*="Название"]');
445        if (titleInput) {
446            productInfo.title = titleInput.value || '';
447        }
448        
449        // Получаем состав товара (ищем textarea с составом)
450        const compositionTextarea = document.querySelector('textarea[placeholder*="Состав"], textarea[name*="composition"]');
451        if (compositionTextarea && compositionTextarea.value) {
452            productInfo.composition = compositionTextarea.value;
453        }
454        
455        console.log('Wildberries Description Generator: Информация о товаре собрана', productInfo);
456        return productInfo;
457    }
458
459    // Функция для анализа использованных ключей и расчета популярности
460    async function analyzeUsedKeywords(description) {
461        console.log('Wildberries Description Generator: Анализ использованных ключей');
462        
463        const analyticsDataStr = await GM.getValue('wb_analytics_data', '[]');
464        const analyticsData = JSON.parse(analyticsDataStr);
465        
466        const descriptionLower = description.toLowerCase();
467        const usedQueries = [];
468        let totalPopularity = 0;
469        
470        // Проходим по всем собранным запросам
471        analyticsData.forEach(keywordData => {
472            keywordData.queries.forEach(queryData => {
473                const query = queryData.query.toLowerCase();
474                
475                // Проверяем, используется ли запрос в описании
476                if (descriptionLower.includes(query)) {
477                    // Парсим популярность (убираем пробелы)
478                    const popularityStr = queryData.popularity.replace(/\s/g, '');
479                    const popularity = parseInt(popularityStr) || 0;
480                    
481                    usedQueries.push({
482                        query: queryData.query,
483                        popularity: popularity
484                    });
485                    
486                    totalPopularity += popularity;
487                }
488            });
489        });
490        
491        console.log(`Wildberries Description Generator: Использовано ${usedQueries.length} запросов`);
492        console.log(`Wildberries Description Generator: Общая популярность: ${totalPopularity}`);
493        
494        return {
495            usedQueries,
496            totalPopularity,
497            totalQueriesAvailable: analyticsData.reduce((sum, kd) => sum + kd.queries.length, 0)
498        };
499    }
500
501    // Функция для форматирования числа с разделителями
502    function formatNumber(num) {
503        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
504    }
505
506    // Функция для генерации описания
507    async function generateDescription(modal) {
508        const keywordsInput = document.getElementById('wb-keywords-input');
509        const minusWordsInput = document.getElementById('wb-minus-words-input');
510        const generateBtn = document.getElementById('wb-generate-btn');
511        const regenerateBtn = document.getElementById('wb-regenerate-btn');
512        const insertBtn = document.getElementById('wb-insert-btn');
513        const resultContainer = document.getElementById('wb-desc-result-container');
514        const resultDiv = document.getElementById('wb-desc-result');
515        const charCountDiv = document.getElementById('wb-char-count');
516        const statusContainer = document.getElementById('wb-desc-status-container');
517        
518        const keywordsText = keywordsInput.value.trim();
519        const minusWordsText = minusWordsInput.value.trim();
520        
521        if (!keywordsText) {
522            showStatus(statusContainer, 'Пожалуйста, введите ключевые слова', 'error');
523            return;
524        }
525        
526        const keywords = keywordsText.split('\n').map(k => k.trim()).filter(k => k);
527        const minusWords = minusWordsText.split('\n').map(k => k.trim().toLowerCase()).filter(k => k);
528        
529        if (keywords.length === 0) {
530            showStatus(statusContainer, 'Пожалуйста, введите хотя бы одно ключевое слово', 'error');
531            return;
532        }
533        
534        // Показываем загрузку
535        generateBtn.disabled = true;
536        regenerateBtn.style.display = 'none';
537        insertBtn.style.display = 'none';
538        resultContainer.style.display = 'none';
539        
540        showStatus(statusContainer, 'Сбор данных с аналитики...', 'info');
541        
542        try {
543            // Собираем данные с аналитики
544            const analyticsData = await collectAnalyticsData(keywords, minusWords);
545            
546            showStatus(statusContainer, 'Генерация описания с помощью AI...', 'info');
547            
548            // Получаем информацию о товаре
549            const productInfo = getProductInfo();
550            
551            // Формируем промпт для AI
552            const prompt = `Создай SEO-оптимизированное описание товара для маркетплейса Wildberries.
553
554ВАЖНО: Это описание для внутренней SEO-оптимизации, его не увидят покупатели. Максимальная длина - 5000 символов.
555
556Информация о товаре:
557${productInfo.title ? `Название: ${productInfo.title}` : ''}
558${productInfo.currentDescription ? `Текущее описание: ${productInfo.currentDescription.substring(0, 500)}` : ''}
559${productInfo.composition ? `\nСостав товара:\n${productInfo.composition}\n` : ''}
560
561Ключевые слова для оптимизации:
562${keywords.join(', ')}
563
564${minusWords.length > 0 ? `Минус-слова (НЕ использовать): ${minusWords.join(', ')}` : ''}
565
566Данные из аналитики Wildberries:
567${JSON.stringify(analyticsData, null, 2)}
568
569СТРОГИЕ ТРЕБОВАНИЯ:
570
5711. ФОРМАТ ОТВЕТА: Выдай только готовый текст без вступлений, заголовков и пояснений. Начинай сразу с описания товара.
572
5732. ИСПОЛЬЗОВАНИЕ СОСТАВА: 
574   ОБЯЗАТЕЛЬНО используй ингредиенты из блока "Состав товара" в описании!
575   - Выбирай только значимые ингредиенты, которые интересны покупателям
576   - Интегрируй их в предложения с ключевыми запросами
577   - Например: "Магний цитрат с витамином B6", "Экстракт женьшеня для энергии", "Цинк для иммунитета"
578   - НЕ используй вспомогательные компоненты (тальк, желатин, стеараты и т.д.)
579
5803. СТРУКТУРА ПРЕДЛОЖЕНИЙ - КРИТИЧЕСКИ ВАЖНО:
581   КАЖДОЕ предложение ОБЯЗАТЕЛЬНО должно содержать:
582   [Ключевой запрос из аналитики] + [ингредиент из состава] + [что делает] + [какой эффект]
583   
584   Примеры правильных предложений:
585   - "Магний цитрат с витамином B6 – органическая форма магния, обладающая высокой биодоступностью."
586   - "Магний с витамином В6 является важнейшим элементом организма взрослых в борьбе со повседневным стрессом."
587   - "Коллаген для суставов с гиалуроновой кислотой способствует восстановлению хрящевой ткани и улучшает подвижность."
588   
589   ВАЖНО: Используй КАЖДЫЙ релевантный запрос из списка аналитики! Не пропускай запросы!
590
5914. ПРИОРИТЕТ ПРОСТЫХ ЗАПРОСОВ:
592   ОБЯЗАТЕЛЬНО используй простые запросы, которые точно совпадают с ключевыми словами!
593   - Если ключевое слово "магний" - ОБЯЗАТЕЛЬНО используй запрос "магний"
594   - Если ключевое слово "коллаген" - ОБЯЗАТЕЛЬНО используй запрос "коллаген"
595   - Если ключевое слово "для суставов" - ОБЯЗАТЕЛЬНО используй запрос "для суставов"
596   Эти простые запросы обычно имеют самую высокую популярность и КРИТИЧЕСКИ важны для SEO!
597
5985. СОРТИРОВКА ПО ПОПУЛЯРНОСТИ: Располагай предложения в порядке убывания популярности из аналитики. Начинай с запросов, которые имеют наибольшую популярность.
599
6006. УНИКАЛЬНОСТЬ ФРАЗ: Каждая ключевая фраза используется ТОЛЬКО ОДИН РАЗ:
601   ✓ "сыворотка для лица" - 1 раз
602   ✓ "увлажняющая сыворотка для лица" - 1 раз
603   ✗ НЕ повторяй одинаковые фразы
604
6057. ИСКЛЮЧЕНИЕ КОНКУРЕНТОВ И НЕРЕЛЕВАНТНЫХ ЗАПРОСОВ: 
606   Автоматически НЕ используй запросы, которые:
607   ✗ Содержат названия брендов конкурентов (mixit, nivea, garnier, loreal, эвалар, зубарева, солгар, now foods, доппельгерц и т.д.)
608   ✗ Содержат фамилии (зубарева, малышева, агапкин и т.д.)
609   ✗ Содержат имена собственные или названия компаний
610   ✗ Не относятся к товару (например, "магнит" вместо "магний", "аптека" как место покупки)
611   ✗ Содержат названия магазинов (магнит, пятерочка, wildberries, ozon, аптека и т.д.)
612   ✗ Содержат слова "купить", "цена", "отзывы", "инструкция", "заказать", "доставка" (это поисковые намерения, а не характеристики)
613   ✗ Являются опечатками или нерелевантными вариациями основного ключа
614   ✗ Содержат латинские буквы или английские слова (кроме общепринятых обозначений типа B6, D3)
615   
616   ПРАВИЛО БРЕНДА: Если в запросе есть слово, которое выглядит как название бренда (начинается с заглавной буквы, редко употребляется, похоже на фамилию или название компании) - НЕ используй этот запрос!
617   
618   Используй только запросы, которые описывают характеристики, свойства и применение товара на РУССКОМ языке.
619
6208. МАКСИМАЛЬНОЕ ИСПОЛЬЗОВАНИЕ ЗАПРОСОВ:
621   КРИТИЧЕСКИ ВАЖНО: Используй КАЖДЫЙ релевантный запрос из данных аналитики!
622   - Проходи по ВСЕМ запросам из списка аналитики по порядку популярности
623   - Для КАЖДОГО запроса создавай отдельное предложение
624   - НЕ пропускай запросы! Твоя цель - использовать максимум запросов
625   - Если запрос релевантен и не содержит брендов - ОБЯЗАТЕЛЬНО используй его
626   - Продолжай писать предложения, пока не закончатся релевантные запросы или не достигнешь 4000 символов
627
6289. ОБЪЕМ ТЕКСТА: 
629   Создай текст на 3800-4000 символов, используя МАКСИМАЛЬНОЕ количество запросов из аналитики.
630   Чем больше запросов используешь - тем лучше для SEO!
631
63210. ЯЗЫК: 
633   КРИТИЧЕСКИ ВАЖНО: Пиши ТОЛЬКО на русском языке!
634   
635   ✗ СТРОГО ЗАПРЕЩЕНО использовать английские слова:
636   - essential → незаменимый, важный, ключевой
637   - crucial → критически важный, ключевой, важнейший
638   - hundreds → сотни
639   - magnesium → магний
640   - protein → белок
641   - amino acids → аминокислоты
642   
643   ✗ ЗАПРЕЩЕНО использовать латинские буквы (кроме обозначений витаминов: B6, D3, C, E, A, K)
644   ✓ РАЗРЕШЕНЫ только обозначения витаминов латиницей: B1, B2, B3, B6, B12, D, D3, C, E, A, K, PP
645   ✓ Все остальное - ТОЛЬКО русскими буквами
646   
647   ПРАВИЛО: Если не знаешь русский перевод слова - используй другое русское слово с похожим значением!
648   Примеры замены:
649   - "essential amino acids" → "незаменимые аминокислоты" или "важнейшие аминокислоты"
650   - "crucial role" → "ключевая роль" или "важнейшая роль"
651   - "optimal ratio" → "оптимальное соотношение" или "идеальная пропорция"
652   
653   Если в запросе из аналитики есть английские слова - НЕ используй этот запрос!
654   Используй только запросы на чистом русском языке.
655
65611. ЕСТЕСТВЕННОСТЬ: Текст должен быть читабельным и естественным, несмотря на высокую плотность ключевых слов.
657
658КРИТИЧЕСКИ ВАЖНО: 
659- Используй КАЖДЫЙ релевантный запрос из аналитики (не пропускай!)
660- В КАЖДОМ предложении должен быть запрос из аналитики + ингредиент из состава
661- Цель: максимальная видимость = максимум использованных запросов
662- Пиши ТОЛЬКО на русском языке, исключая бренды и фамилии
663
664ИНСТРУКЦИЯ ПО ИСПОЛЬЗОВАНИЮ ЗАПРОСОВ:
665Проходи по списку запросов из аналитики ПОСЛЕДОВАТЕЛЬНО и для КАЖДОГО запроса:
6661. Проверь, содержит ли запрос бренды/фамилии/английские слова - если ДА, пропусти
6672. Если запрос чистый - ОБЯЗАТЕЛЬНО создай для него предложение
6683. Продолжай, пока не используешь ВСЕ релевантные запросы или не достигнешь 4000 символов
669
670Твоя цель - использовать МИНИМУМ 60-70% от всех доступных запросов!
671
672Сгенерируй описание:`;
673
674            console.log('Wildberries Description Generator: Отправка запроса к AI');
675            
676            // Генерируем описание с помощью AI
677            const description = await RM.aiCall(prompt);
678            
679            console.log('Wildberries Description Generator: Описание сгенерировано');
680            
681            // Анализируем использованные ключи
682            const analysis = await analyzeUsedKeywords(description);
683            
684            // Проверяем длину
685            const charCount = description.length;
686            
687            // Сохраняем описание
688            await GM.setValue('wb_generated_description', description);
689            
690            // Показываем результат
691            resultDiv.textContent = description;
692            resultContainer.style.display = 'block';
693            
694            // Обновляем счетчик символов с информацией о ключах
695            const popularityInfo = `${charCount} / 5000 символов | Использовано ${analysis.usedQueries.length} из ${analysis.totalQueriesAvailable} запросов | Общая популярность: ${formatNumber(analysis.totalPopularity)}`;
696            charCountDiv.textContent = popularityInfo;
697            
698            // Показываем кнопки
699            generateBtn.style.display = 'none';
700            regenerateBtn.style.display = 'inline-block';
701            insertBtn.style.display = 'inline-block';
702            
703            showStatus(statusContainer, `Описание успешно сгенерировано! (${charCount} символов)`, 'success');
704            
705        } catch (error) {
706            console.error('Wildberries Description Generator: Ошибка при генерации описания:', error);
707            showStatus(statusContainer, 'Ошибка при генерации описания. Попробуйте еще раз.', 'error');
708        } finally {
709            generateBtn.disabled = false;
710        }
711    }
712
713    // Функция для показа статуса
714    function showStatus(container, message, type) {
715        container.innerHTML = `<div class="wb-desc-status ${type}">${message}</div>`;
716    }
717
718    // Функция для вставки описания
719    async function insertDescription(modal) {
720        console.log('Wildberries Description Generator: Вставка описания');
721        
722        try {
723            const description = await GM.getValue('wb_generated_description', '');
724            
725            if (!description) {
726                alert('Описание не найдено. Пожалуйста, сгенерируйте описание сначала.');
727                return;
728            }
729            
730            // Находим поле описания
731            const descriptionTextarea = document.querySelector('textarea[data-testid="card-form-main-field-description"]');
732            
733            if (!descriptionTextarea) {
734                alert('Не удалось найти поле описания. Убедитесь, что вы находитесь на странице редактирования товара.');
735                return;
736            }
737            
738            // Вставляем описание
739            descriptionTextarea.value = description;
740            descriptionTextarea.dispatchEvent(new Event('input', { bubbles: true }));
741            descriptionTextarea.dispatchEvent(new Event('change', { bubbles: true }));
742            
743            console.log('Wildberries Description Generator: Описание успешно вставлено');
744            
745            // Закрываем модальное окно
746            modal.remove();
747            
748            alert('Описание успешно вставлено!');
749            
750        } catch (error) {
751            console.error('Wildberries Description Generator: Ошибка при вставке описания:', error);
752            alert('Ошибка при вставке описания. Попробуйте еще раз.');
753        }
754    }
755
756    // Функция для инициализации расширения
757    function init() {
758        console.log('Wildberries Description Generator: Инициализация');
759        
760        // Проверяем, что мы на странице редактирования товара
761        if (window.location.href.includes('seller.wildberries.ru/new-goods/card')) {
762            
763            // Ждем загрузки страницы и добавляем кнопку
764            const observer = new MutationObserver((mutations, obs) => {
765                const descriptionHeader = document.querySelector('.Description-header__zK-9sKs8RX');
766                if (descriptionHeader) {
767                    createGeneratorButton();
768                    obs.disconnect();
769                }
770            });
771            
772            observer.observe(document.body, {
773                childList: true,
774                subtree: true
775            });
776            
777            // Также пробуем добавить кнопку сразу
778            setTimeout(createGeneratorButton, 2000);
779        }
780        
781        // Проверяем, что мы на странице аналитики и запускаем автосбор
782        if (window.location.href.includes('seller.wildberries.ru/search-analytics/popular-search-queries')) {
783            setTimeout(autoCollectOnAnalyticsPage, 2000);
784        }
785    }
786
787    // Запускаем инициализацию
788    if (document.readyState === 'loading') {
789        document.addEventListener('DOMContentLoaded', init);
790    } else {
791        init();
792    }
793
794})();
Wildberries Description Generator | Robomonkey