Генератор 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="Например: витамины иммунитет здоровье"></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="Например: mixit nivea магнит"></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})();