Автоматическая установка стратегии рекламной кампании на основе статистики
Size
103.6 KB
Version
1.8.27
Created
Feb 10, 2026
Updated
about 1 month ago
1// ==UserScript==
2// @name MP Manager Auto Strategy Setter
3// @description Автоматическая установка стратегии рекламной кампании на основе статистики
4// @version 1.8.27
5// @match https://*.app.mpmgr.ru/*
6// @icon https://app.mpmgr.ru/favicon.ico
7// @grant GM.getValue
8// @grant GM.setValue
9// @grant GM.deleteValue
10// @grant GM.openInTab
11// @grant GM.setClipboard
12// ==/UserScript==
13(function() {
14 'use strict';
15
16 console.log('MP Manager Auto Strategy Setter загружен');
17
18 // Константы
19 const STRATEGY_CODE = 'eyJjYW1wYWlnblR5cGUiOiJTZWFyY2hDYXRhbG9nIiwidmVyc2lvbiI6MiwiZGF0YSI6eyJzdGF0ZSI6IkVuYWJsZWQiLCJtb2RlIjp7InR5cGUiOiJQb3NpdGlvbiIsInBhdXNlVHlwZSI6IlJlc3VtZSJ9LCJzdHJhdGVneSI6eyJ0eXBlIjoiQ2FtcGFpZ25TdGF0cyIsIm1vZGUiOiJQbGFjZSIsImJpZE1vZGUiOiJDb3N0UGVyQ2xpY2siLCJjYW1wYWlnblN0YXRzIjp7Im1heFByaWNlIjo2MCwibWluUHJpY2UiOjEsInBsYXRmb3JtcyI6W10sImludGVydmFsIjoiV2VlayIsInJ1bGVzIjpbeyJ0eXBlIjoiQ29zdFBlckFkZGVkVG9DYXJ0IiwibW9kZSI6IlZhbHVlIiwidmFsdWUiOiIifV19fSwicGxhY2VTdHJhdGVneSI6eyJ0eXBlIjoiS2V5d29yZHMiLCJrZXl3b3JkcyI6eyJrZXl3b3JkcyI6W119fSwiaXNDbHVzdGVyIjpmYWxzZX19';
20 const CLUSTER_STRATEGY_CODE = 'eyJjYW1wYWlnblR5cGUiOiJTZWFyY2hDYXRhbG9nIiwidmVyc2lvbiI6MiwiZGF0YSI6eyJzdGF0ZSI6IkVuYWJsZWQiLCJtb2RlIjp7InR5cGUiOiJQb3NpdGlvbiIsInBhdXNlVHlwZSI6IlJlc3VtZSJ9LCJzdHJhdGVneSI6eyJ0eXBlIjoiQ2FtcGFpZ25TdGF0cyIsIm1vZGUiOiJQbGFjZSIsImJpZE1vZGUiOiJDb3N0UGVyQ2xpY2siLCJjbHVzdGVyU3RhdHMiOnsibWF4QmlkIjo4MDAwLCJtaW5CaWQiOjQwMCwiaW50ZXJ2YWwiOiJUaHJlZURheXMiLCJydWxlcyI6W3sidHlwZSI6IkF2ZXJhZ2VQb3NpdGlvbiIsIm1vZGUiOiJWYWx1ZSIsInZhbHVlIjoyfSx7InR5cGUiOiJDb3N0UGVyQWRkZWRUb0NhcnQiLCJtb2RlIjoiQXZlcmFnZVZhbHVlIn1dfSwiY2FtcGFpZ25TdGF0cyI6eyJtYXhCaWQiOjgwMDAsIm1pbkJpZCI6NDAwLCJpbnRlcnZhbCI6IldlZWsiLCJydWxlcyI6W3sidHlwZSI6IkNvc3RQZXJBZGRlZFRvQ2FydCIsIm1vZGUiOiJWYWx1ZSIsInZhbHVlIjoiIn1dLCJtYXhQcmljZSI6IjEwMCIsIm1pblByaWNlIjoiMSIsInBsYXRmb3JtcyI6W119fSwicGxhY2VTdHJhdGVneSI6eyJ0eXBlIjoiS2V5d29yZHMiLCJrZXl3b3JkcyI6eyJrZXl3b3JkcyI6W119fSwiaXNDbHVzdGVyIjp0cnVlfX0=';
21
22 // Глобальные переменные для массовой обработки
23 let isBulkProcessing = false;
24 let bulkDesiredPercentage = 50;
25 let bulkPaused = false;
26
27 // Функция для получения сохраненных стратегий
28 async function getSavedStrategies() {
29 let strategies = await GM.getValue('saved_strategies', []);
30
31 // Миграция: добавляем ID к стратегиям, у которых его нет
32 let needsMigration = false;
33 strategies = strategies.map((strategy, index) => {
34 if (!strategy.id) {
35 needsMigration = true;
36 return {
37 ...strategy,
38 id: 'strategy_migrated_' + Date.now() + '_' + index
39 };
40 }
41 return strategy;
42 });
43
44 // Сохраняем обновленные стратегии, если была миграция
45 if (needsMigration) {
46 await GM.setValue('saved_strategies', strategies);
47 console.log('Миграция стратегий выполнена: добавлены ID');
48 }
49
50 // Если нет сохраненных стратегий, добавляем стратегию по умолчанию
51 if (strategies.length === 0) {
52 return [{
53 id: 'default',
54 name: 'Стратегия по умолчанию',
55 data: STRATEGY_CODE
56 }];
57 }
58 return strategies;
59 }
60
61 // Функция для получения сохраненных стратегий для кластеров
62 async function getSavedClusterStrategies() {
63 let strategies = await GM.getValue('saved_cluster_strategies', []);
64
65 // Миграция: добавляем ID к стратегиям, у которых его нет
66 let needsMigration = false;
67 strategies = strategies.map((strategy, index) => {
68 if (!strategy.id) {
69 needsMigration = true;
70 return {
71 ...strategy,
72 id: 'cluster_strategy_migrated_' + Date.now() + '_' + index
73 };
74 }
75 return strategy;
76 });
77
78 // Сохраняем обновленные стратегии, если была миграция
79 if (needsMigration) {
80 await GM.setValue('saved_cluster_strategies', strategies);
81 console.log('Миграция кластерных стратегий выполнена: добавлены ID');
82 }
83
84 // Если нет сохраненных стратегий, добавляем стратегию по умолчанию
85 if (strategies.length === 0) {
86 return [{
87 id: 'cluster_default',
88 name: 'Кластерная стратегия по умолчанию',
89 data: CLUSTER_STRATEGY_CODE
90 }];
91 }
92 return strategies;
93 }
94
95 // Функция для получения текущей стратегии
96 async function getCurrentStrategy() {
97 const currentId = await GM.getValue('current_strategy_id', 'default');
98 const strategies = await getSavedStrategies();
99 const strategy = strategies.find(s => s.id === currentId);
100 return strategy ? strategy.data : STRATEGY_CODE;
101 }
102
103 // Функция для получения текущей кластерной стратегии
104 async function getCurrentClusterStrategy() {
105 const currentId = await GM.getValue('current_cluster_strategy_id', 'cluster_default');
106 const strategies = await getSavedClusterStrategies();
107 const strategy = strategies.find(s => s.id === currentId);
108 return strategy ? strategy.data : CLUSTER_STRATEGY_CODE;
109 }
110
111 // Функция для создания модального окна управления стратегиями
112 function createStrategyManagementModal() {
113 return new Promise(async (resolve) => {
114 // Создаем оверлей
115 const overlay = document.createElement('div');
116 overlay.style.cssText = `
117 position: fixed;
118 top: 0;
119 left: 0;
120 width: 100%;
121 height: 100%;
122 background: rgba(0, 0, 0, 0.5);
123 display: flex;
124 justify-content: center;
125 align-items: center;
126 z-index: 10001;
127 `;
128
129 // Создаем модальное окно
130 const modal = document.createElement('div');
131 modal.style.cssText = `
132 background: white;
133 border-radius: 12px;
134 padding: 24px;
135 min-width: 500px;
136 max-width: 600px;
137 max-height: 80vh;
138 overflow-y: auto;
139 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
140 `;
141
142 // Заголовок
143 const title = document.createElement('h2');
144 title.textContent = 'Управление стратегиями';
145 title.style.cssText = `
146 margin: 0 0 20px 0;
147 font-size: 24px;
148 font-weight: bold;
149 color: #333;
150 `;
151 modal.appendChild(title);
152
153 // Контейнер для списка стратегий
154 const listContainer = document.createElement('div');
155 listContainer.style.cssText = `
156 margin-bottom: 20px;
157 max-height: 300px;
158 overflow-y: auto;
159 `;
160
161 // Функция для обновления списка
162 const updateList = async () => {
163 const currentStrategies = await getSavedStrategies();
164 const selectedStrategy = await GM.getValue('current_strategy_id', 'default');
165 listContainer.innerHTML = '';
166
167 if (currentStrategies.length === 0) {
168 const emptyMessage = document.createElement('p');
169 emptyMessage.textContent = 'Нет сохраненных стратегий. Используется стратегия по умолчанию.';
170 emptyMessage.style.cssText = `
171 color: #666;
172 font-style: italic;
173 padding: 20px;
174 text-align: center;
175 `;
176 listContainer.appendChild(emptyMessage);
177 } else {
178 currentStrategies.forEach((strategy, index) => {
179 const item = document.createElement('div');
180 const isSelected = strategy.id === selectedStrategy;
181 item.style.cssText = `
182 display: flex;
183 justify-content: space-between;
184 align-items: center;
185 padding: 12px;
186 margin-bottom: 8px;
187 background: ${isSelected ? '#e3f2fd' : '#f5f5f5'};
188 border-radius: 8px;
189 border: ${isSelected ? '2px solid #2196f3' : '1px solid #ddd'};
190 `;
191
192 const nameSpan = document.createElement('span');
193 nameSpan.textContent = strategy.name + (isSelected ? ' ✓' : '');
194 nameSpan.style.cssText = `
195 font-size: 16px;
196 color: #333;
197 flex: 1;
198 font-weight: ${isSelected ? 'bold' : 'normal'};
199 `;
200
201 const buttonsContainer = document.createElement('div');
202 buttonsContainer.style.cssText = `
203 display: flex;
204 gap: 8px;
205 `;
206
207 // Кнопка выбора
208 if (!isSelected) {
209 const selectBtn = document.createElement('button');
210 selectBtn.textContent = '✓ Выбрать';
211 selectBtn.style.cssText = `
212 background: #2196f3;
213 color: white;
214 border: none;
215 padding: 6px 12px;
216 border-radius: 6px;
217 cursor: pointer;
218 font-size: 14px;
219 `;
220 selectBtn.onmouseover = () => selectBtn.style.background = '#1976d2';
221 selectBtn.onmouseout = () => selectBtn.style.background = '#2196f3';
222 selectBtn.onclick = async () => {
223 await GM.setValue('current_strategy_id', strategy.id);
224 console.log(`Выбрана стратегия: ${strategy.name} (ID: ${strategy.id})`);
225 await updateList();
226 };
227 buttonsContainer.appendChild(selectBtn);
228 }
229
230 // Кнопка удаления
231 const deleteBtn = document.createElement('button');
232 deleteBtn.textContent = '🗑️';
233 deleteBtn.style.cssText = `
234 background: #f44336;
235 color: white;
236 border: none;
237 padding: 6px 12px;
238 border-radius: 6px;
239 cursor: pointer;
240 font-size: 14px;
241 `;
242 deleteBtn.onmouseover = () => deleteBtn.style.background = '#d32f2f';
243 deleteBtn.onmouseout = () => deleteBtn.style.background = '#f44336';
244 deleteBtn.onclick = async () => {
245 if (confirm(`Удалить стратегию "${strategy.name}"?`)) {
246 currentStrategies.splice(index, 1);
247 await GM.setValue('saved_strategies', currentStrategies);
248 // Если удаляем выбранную стратегию, возвращаемся к дефолтной
249 if (isSelected) {
250 await GM.setValue('current_strategy_id', 'default');
251 }
252 await updateList();
253 }
254 };
255 buttonsContainer.appendChild(deleteBtn);
256
257 item.appendChild(nameSpan);
258 item.appendChild(buttonsContainer);
259 listContainer.appendChild(item);
260 });
261 }
262 };
263
264 await updateList();
265 modal.appendChild(listContainer);
266
267 // Кнопка добавления стратегии
268 const addButton = document.createElement('button');
269 addButton.textContent = '➕ Добавить новую стратегию';
270 addButton.style.cssText = `
271 width: 100%;
272 background: #4caf50;
273 color: white;
274 border: none;
275 padding: 12px;
276 border-radius: 8px;
277 cursor: pointer;
278 font-size: 16px;
279 font-weight: bold;
280 margin-bottom: 12px;
281 `;
282 addButton.onmouseover = () => addButton.style.background = '#45a049';
283 addButton.onmouseout = () => addButton.style.background = '#4caf50';
284 addButton.onclick = async () => {
285 const name = prompt('Введите название стратегии:');
286 if (!name) return;
287
288 const data = prompt('Вставьте код стратегии (скопируйте из MP Manager):');
289 if (!data) return;
290
291 const currentStrategies = await GM.getValue('saved_strategies', []);
292 const newId = 'strategy_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
293 currentStrategies.push({ id: newId, name, data });
294 await GM.setValue('saved_strategies', currentStrategies);
295 alert(`Стратегия "${name}" сохранена!`);
296 await updateList();
297 };
298 modal.appendChild(addButton);
299
300 // Кнопка закрытия
301 const closeButton = document.createElement('button');
302 closeButton.textContent = 'Закрыть';
303 closeButton.style.cssText = `
304 width: 100%;
305 background: #666;
306 color: white;
307 border: none;
308 padding: 12px;
309 border-radius: 8px;
310 cursor: pointer;
311 font-size: 16px;
312 `;
313 closeButton.onmouseover = () => closeButton.style.background = '#555';
314 closeButton.onmouseout = () => closeButton.style.background = '#666';
315 closeButton.onclick = () => {
316 document.body.removeChild(overlay);
317 resolve();
318 };
319 modal.appendChild(closeButton);
320
321 overlay.appendChild(modal);
322 document.body.appendChild(overlay);
323
324 // Закрытие по клику на оверлей
325 overlay.onclick = (e) => {
326 if (e.target === overlay) {
327 document.body.removeChild(overlay);
328 resolve();
329 }
330 };
331 });
332 }
333
334 // Функция для создания модального окна управления кластерными стратегиями
335 function createClusterStrategyManagementModal() {
336 return new Promise(async (resolve) => {
337 // Создаем оверлей
338 const overlay = document.createElement('div');
339 overlay.style.cssText = `
340 position: fixed;
341 top: 0;
342 left: 0;
343 width: 100%;
344 height: 100%;
345 background: rgba(0, 0, 0, 0.5);
346 display: flex;
347 justify-content: center;
348 align-items: center;
349 z-index: 10001;
350 `;
351
352 // Создаем модальное окно
353 const modal = document.createElement('div');
354 modal.style.cssText = `
355 background: white;
356 border-radius: 12px;
357 padding: 24px;
358 min-width: 500px;
359 max-width: 600px;
360 max-height: 80vh;
361 overflow-y: auto;
362 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
363 `;
364
365 // Заголовок
366 const title = document.createElement('h2');
367 title.textContent = 'Управление стратегиями для кластеров';
368 title.style.cssText = `
369 margin: 0 0 20px 0;
370 font-size: 24px;
371 font-weight: bold;
372 color: #333;
373 `;
374 modal.appendChild(title);
375
376 // Контейнер для списка стратегий
377 const listContainer = document.createElement('div');
378 listContainer.style.cssText = `
379 margin-bottom: 20px;
380 max-height: 300px;
381 overflow-y: auto;
382 `;
383
384 // Функция для обновления списка
385 const updateList = async () => {
386 const currentStrategies = await getSavedClusterStrategies();
387 const selectedStrategy = await GM.getValue('current_cluster_strategy_id', 'cluster_default');
388 listContainer.innerHTML = '';
389
390 if (currentStrategies.length === 0) {
391 const emptyMessage = document.createElement('p');
392 emptyMessage.textContent = 'Нет сохраненных стратегий. Используется стратегия по умолчанию.';
393 emptyMessage.style.cssText = `
394 color: #666;
395 font-style: italic;
396 padding: 20px;
397 text-align: center;
398 `;
399 listContainer.appendChild(emptyMessage);
400 } else {
401 currentStrategies.forEach((strategy, index) => {
402 const item = document.createElement('div');
403 const isSelected = strategy.id === selectedStrategy;
404 item.style.cssText = `
405 display: flex;
406 justify-content: space-between;
407 align-items: center;
408 padding: 12px;
409 margin-bottom: 8px;
410 background: ${isSelected ? '#e3f2fd' : '#f5f5f5'};
411 border-radius: 8px;
412 border: ${isSelected ? '2px solid #2196f3' : '1px solid #ddd'};
413 `;
414
415 const nameSpan = document.createElement('span');
416 nameSpan.textContent = strategy.name + (isSelected ? ' ✓' : '');
417 nameSpan.style.cssText = `
418 font-size: 16px;
419 color: #333;
420 flex: 1;
421 font-weight: ${isSelected ? 'bold' : 'normal'};
422 `;
423
424 const buttonsContainer = document.createElement('div');
425 buttonsContainer.style.cssText = `
426 display: flex;
427 gap: 8px;
428 `;
429
430 // Кнопка выбора
431 if (!isSelected) {
432 const selectBtn = document.createElement('button');
433 selectBtn.textContent = '✓ Выбрать';
434 selectBtn.style.cssText = `
435 background: #2196f3;
436 color: white;
437 border: none;
438 padding: 6px 12px;
439 border-radius: 6px;
440 cursor: pointer;
441 font-size: 14px;
442 `;
443 selectBtn.onmouseover = () => selectBtn.style.background = '#1976d2';
444 selectBtn.onmouseout = () => selectBtn.style.background = '#2196f3';
445 selectBtn.onclick = async () => {
446 await GM.setValue('current_cluster_strategy_id', strategy.id);
447 console.log(`Выбрана кластерная стратегия: ${strategy.name} (ID: ${strategy.id})`);
448 await updateList();
449 };
450 buttonsContainer.appendChild(selectBtn);
451 }
452
453 // Кнопка удаления
454 const deleteBtn = document.createElement('button');
455 deleteBtn.textContent = '🗑️';
456 deleteBtn.style.cssText = `
457 background: #f44336;
458 color: white;
459 border: none;
460 padding: 6px 12px;
461 border-radius: 6px;
462 cursor: pointer;
463 font-size: 14px;
464 `;
465 deleteBtn.onmouseover = () => deleteBtn.style.background = '#d32f2f';
466 deleteBtn.onmouseout = () => deleteBtn.style.background = '#f44336';
467 deleteBtn.onclick = async () => {
468 if (confirm(`Удалить стратегию "${strategy.name}"?`)) {
469 currentStrategies.splice(index, 1);
470 await GM.setValue('saved_cluster_strategies', currentStrategies);
471 // Если удаляем выбранную стратегию, возвращаемся к дефолтной
472 if (isSelected) {
473 await GM.setValue('current_cluster_strategy_id', 'cluster_default');
474 }
475 await updateList();
476 }
477 };
478 buttonsContainer.appendChild(deleteBtn);
479
480 item.appendChild(nameSpan);
481 item.appendChild(buttonsContainer);
482 listContainer.appendChild(item);
483 });
484 }
485 };
486
487 await updateList();
488 modal.appendChild(listContainer);
489
490 // Кнопка добавления стратегии
491 const addButton = document.createElement('button');
492 addButton.textContent = '➕ Добавить новую стратегию';
493 addButton.style.cssText = `
494 width: 100%;
495 background: #4caf50;
496 color: white;
497 border: none;
498 padding: 12px;
499 border-radius: 8px;
500 cursor: pointer;
501 font-size: 16px;
502 font-weight: bold;
503 margin-bottom: 12px;
504 `;
505 addButton.onmouseover = () => addButton.style.background = '#45a049';
506 addButton.onmouseout = () => addButton.style.background = '#4caf50';
507 addButton.onclick = async () => {
508 const name = prompt('Введите название стратегии для кластеров:');
509 if (!name) return;
510
511 const data = prompt('Вставьте код стратегии (скопируйте из MP Manager):');
512 if (!data) return;
513
514 const currentStrategies = await GM.getValue('saved_cluster_strategies', []);
515 const newId = 'cluster_strategy_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
516 currentStrategies.push({ id: newId, name, data });
517 await GM.setValue('saved_cluster_strategies', currentStrategies);
518 alert(`Кластерная стратегия "${name}" сохранена!`);
519 await updateList();
520 };
521 modal.appendChild(addButton);
522
523 // Кнопка закрытия
524 const closeButton = document.createElement('button');
525 closeButton.textContent = 'Закрыть';
526 closeButton.style.cssText = `
527 width: 100%;
528 background: #666;
529 color: white;
530 border: none;
531 padding: 12px;
532 border-radius: 8px;
533 cursor: pointer;
534 font-size: 16px;
535 `;
536 closeButton.onmouseover = () => closeButton.style.background = '#555';
537 closeButton.onmouseout = () => closeButton.style.background = '#666';
538 closeButton.onclick = () => {
539 document.body.removeChild(overlay);
540 resolve();
541 };
542 modal.appendChild(closeButton);
543
544 overlay.appendChild(modal);
545 document.body.appendChild(overlay);
546
547 // Закрытие по клику на оверлей
548 overlay.onclick = (e) => {
549 if (e.target === overlay) {
550 document.body.removeChild(overlay);
551 resolve();
552 }
553 };
554 });
555 }
556
557 // Функция для создания UI
558 function createUI() {
559 console.log('Создание UI панели');
560
561 // Проверяем, есть ли сохраненный ДРР для массовой обработки
562 GM.getValue('bulkProcessingDRR', null).then(savedDRR => {
563 if (savedDRR !== null) {
564 console.log(`Найден сохраненный ДРР для массовой обработки: ${savedDRR}`);
565 // Автоматически запускаем стратегию с сохраненным ДРР
566 setTimeout(() => {
567 const input = document.getElementById('desired-percentage');
568 if (input) {
569 input.value = savedDRR;
570 // Запускаем стратегию автоматически
571 runStrategy();
572 }
573 }, 2000);
574 }
575 });
576
577 const panel = document.createElement('div');
578 panel.id = 'auto-strategy-panel';
579 panel.style.cssText = `
580 position: fixed;
581 top: 20px;
582 right: 20px;
583 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
584 padding: 15px;
585 border-radius: 12px;
586 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
587 z-index: 10000;
588 min-width: 280px;
589 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
590 color: white;
591 transition: all 0.3s ease;
592 cursor: move;
593 `;
594
595 panel.innerHTML = `
596 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; cursor: pointer;" id="panel-header">
597 <h3 style="margin: 0; font-size: 16px; font-weight: 600;">🚀 Авто-стратегия</h3>
598 <span id="toggle-icon" style="font-size: 18px;">▼</span>
599 </div>
600
601 <div id="panel-content" style="display: none;">
602 <div style="margin-bottom: 15px;">
603 <label style="display: block; margin-bottom: 8px; font-size: 13px; font-weight: 500;">
604 Желаемый % рекламных расходов:
605 </label>
606 <input
607 type="number"
608 id="desired-percentage"
609 value="50"
610 min="1"
611 max="100"
612 step="0.1"
613 style="
614 width: 100%;
615 padding: 10px;
616 border: none;
617 border-radius: 8px;
618 font-size: 14px;
619 box-sizing: border-box;
620 background: rgba(255, 255, 255, 0.95);
621 color: #333;
622 cursor: text;
623 "
624 />
625 </div>
626
627 <div style="margin-bottom: 15px;">
628 <label style="display: flex; align-items: center; cursor: pointer; font-size: 13px; font-weight: 500;">
629 <input
630 type="checkbox"
631 id="apply-to-clusters"
632 checked
633 style="
634 margin-right: 8px;
635 width: 18px;
636 height: 18px;
637 cursor: pointer;
638 "
639 />
640 Применить стратегию к кластерам
641 </label>
642 </div>
643
644 <button
645 id="run-strategy-btn"
646 style="
647 width: 100%;
648 padding: 12px;
649 background: white;
650 color: #667eea;
651 border: none;
652 border-radius: 8px;
653 font-size: 14px;
654 font-weight: 600;
655 cursor: pointer;
656 transition: all 0.3s ease;
657 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
658 margin-bottom: 10px;
659 "
660 onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 16px rgba(0, 0, 0, 0.2)';"
661 onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 12px rgba(0, 0, 0, 0.15)';"
662 >
663 Запустить
664 </button>
665
666 <button
667 id="manage-strategies-btn"
668 style="
669 width: 100%;
670 padding: 10px;
671 background: rgba(255, 255, 255, 0.2);
672 color: white;
673 border: 1px solid rgba(255, 255, 255, 0.3);
674 border-radius: 8px;
675 font-size: 13px;
676 font-weight: 600;
677 cursor: pointer;
678 transition: all 0.3s ease;
679 margin-bottom: 8px;
680 "
681 onmouseover="this.style.background='rgba(255, 255, 255, 0.3)';"
682 onmouseout="this.style.background='rgba(255, 255, 255, 0.2)';"
683 >
684 ⚙️ Управление стратегиями
685 </button>
686
687 <button
688 id="manage-cluster-strategies-btn"
689 style="
690 width: 100%;
691 padding: 10px;
692 background: rgba(255, 255, 255, 0.2);
693 color: white;
694 border: 1px solid rgba(255, 255, 255, 0.3);
695 border-radius: 8px;
696 font-size: 13px;
697 font-weight: 600;
698 cursor: pointer;
699 transition: all 0.3s ease;
700 "
701 onmouseover="this.style.background='rgba(255, 255, 255, 0.3)';"
702 onmouseout="this.style.background='rgba(255, 255, 255, 0.2)';"
703 >
704 🎯 Стратегии для кластеров
705 </button>
706
707 <div id="status-message" style="
708 margin-top: 15px;
709 padding: 10px;
710 border-radius: 8px;
711 font-size: 12px;
712 background: rgba(255, 255, 255, 0.2);
713 display: none;
714 "></div>
715 </div>
716 `;
717
718 document.body.appendChild(panel);
719 console.log('UI панель создана');
720
721 // Добавляем возможность перетаскивания
722 makeDraggable(panel);
723
724 // Добавляем обработчик клика на заголовок для разворачивания/сворачивания
725 document.getElementById('panel-header').addEventListener('click', () => {
726 const content = document.getElementById('panel-content');
727 const icon = document.getElementById('toggle-icon');
728 if (content.style.display === 'none') {
729 content.style.display = 'block';
730 icon.textContent = '▲';
731 } else {
732 content.style.display = 'none';
733 icon.textContent = '▼';
734 }
735 });
736
737 // Добавляем обработчик клика на кнопку
738 document.getElementById('run-strategy-btn').addEventListener('click', runStrategy);
739
740 // Добавляем обработчик для кнопки управления стратегиями
741 document.getElementById('manage-strategies-btn').addEventListener('click', async () => {
742 await createStrategyManagementModal();
743 });
744
745 // Добавляем обработчик для кнопки управления кластерными стратегиями
746 document.getElementById('manage-cluster-strategies-btn').addEventListener('click', async () => {
747 await createClusterStrategyManagementModal();
748 });
749 }
750
751 // Функция для создания UI панели массового изменения
752 function createBulkUI() {
753 console.log('Создание UI панели массового изменения');
754
755 const panel = document.createElement('div');
756 panel.id = 'bulk-strategy-panel';
757 panel.style.cssText = `
758 position: fixed;
759 top: 20px;
760 right: 20px;
761 background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
762 padding: 15px;
763 border-radius: 12px;
764 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
765 z-index: 10000;
766 min-width: 300px;
767 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
768 color: white;
769 transition: all 0.3s ease;
770 cursor: move;
771 `;
772
773 panel.innerHTML = `
774 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; cursor: pointer;" id="bulk-panel-header">
775 <h3 style="margin: 0; font-size: 16px; font-weight: 600;">📦 Массовое изменение</h3>
776 <span id="bulk-toggle-icon" style="font-size: 18px;">▼</span>
777 </div>
778
779 <div id="bulk-panel-content" style="display: none;">
780 <div style="margin-bottom: 15px;">
781 <label style="display: block; margin-bottom: 8px; font-size: 13px; font-weight: 500;">
782 Желаемый % рекламных расходов (ДРР):
783 </label>
784 <input
785 type="number"
786 id="bulk-desired-percentage"
787 value="50"
788 min="1"
789 max="100"
790 step="0.1"
791 style="
792 width: 100%;
793 padding: 10px;
794 border: none;
795 border-radius: 8px;
796 font-size: 14px;
797 box-sizing: border-box;
798 background: rgba(255, 255, 255, 0.95);
799 color: #333;
800 cursor: text;
801 "
802 />
803 </div>
804
805 <div id="bulk-campaigns-info" style="
806 margin-bottom: 15px;
807 padding: 10px;
808 border-radius: 8px;
809 font-size: 12px;
810 background: rgba(255, 255, 255, 0.2);
811 ">
812 Найдено кампаний: <span id="campaigns-count">0</span>
813 </div>
814
815 <button
816 id="run-bulk-strategy-btn"
817 style="
818 width: 100%;
819 padding: 12px;
820 background: white;
821 color: #f5576c;
822 border: none;
823 border-radius: 8px;
824 font-size: 14px;
825 font-weight: 600;
826 cursor: pointer;
827 transition: all 0.3s ease;
828 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
829 margin-bottom: 10px;
830 "
831 onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 16px rgba(0, 0, 0, 0.2)';"
832 onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 12px rgba(0, 0, 0, 0.15)';"
833 >
834 Запустить
835 </button>
836
837 <button
838 id="bulk-manage-strategies-btn"
839 style="
840 width: 100%;
841 padding: 10px;
842 background: rgba(255, 255, 255, 0.2);
843 color: white;
844 border: 1px solid rgba(255, 255, 255, 0.3);
845 border-radius: 8px;
846 font-size: 13px;
847 font-weight: 600;
848 cursor: pointer;
849 transition: all 0.3s ease;
850 margin-bottom: 10px;
851 "
852 onmouseover="this.style.background='rgba(255, 255, 255, 0.3)';"
853 onmouseout="this.style.background='rgba(255, 255, 255, 0.2)';"
854 >
855 ⚙️ Управление стратегиями
856 </button>
857
858 <button
859 id="bulk-manage-cluster-strategies-btn"
860 style="
861 width: 100%;
862 padding: 10px;
863 background: rgba(255, 255, 255, 0.2);
864 color: white;
865 border: 1px solid rgba(255, 255, 255, 0.3);
866 border-radius: 8px;
867 font-size: 13px;
868 font-weight: 600;
869 cursor: pointer;
870 transition: all 0.3s ease;
871 margin-bottom: 10px;
872 "
873 onmouseover="this.style.background='rgba(255, 255, 255, 0.3)';"
874 onmouseout="this.style.background='rgba(255, 255, 255, 0.2)';"
875 >
876 🎯 Стратегии для кластеров
877 </button>
878
879 <div style="margin-bottom: 15px;">
880 <label style="display: flex; align-items: center; cursor: pointer; font-size: 13px; font-weight: 500;">
881 <input
882 type="checkbox"
883 id="bulk-apply-to-clusters"
884 checked
885 style="
886 margin-right: 8px;
887 width: 18px;
888 height: 18px;
889 cursor: pointer;
890 "
891 />
892 Применить стратегию к кластерам
893 </label>
894 </div>
895
896 <div id="bulk-control-buttons" style="
897 margin-top: 10px;
898 display: none;
899 gap: 10px;
900 ">
901 <button
902 id="pause-bulk-btn"
903 style="
904 flex: 1;
905 padding: 10px;
906 background: rgba(255, 255, 255, 0.9);
907 color: #f5576c;
908 border: none;
909 border-radius: 8px;
910 font-size: 13px;
911 font-weight: 600;
912 cursor: pointer;
913 transition: all 0.3s ease;
914 "
915 >
916 ⏸️ Пауза
917 </button>
918 <button
919 id="stop-bulk-btn"
920 style="
921 flex: 1;
922 padding: 10px;
923 background: rgba(255, 59, 48, 0.9);
924 color: white;
925 border: none;
926 border-radius: 8px;
927 font-size: 13px;
928 font-weight: 600;
929 cursor: pointer;
930 transition: all 0.3s ease;
931 "
932 >
933 ⏹️ Стоп
934 </button>
935 </div>
936
937 <div id="bulk-status-message" style="
938 margin-top: 15px;
939 padding: 10px;
940 border-radius: 8px;
941 font-size: 12px;
942 background: rgba(255, 255, 255, 0.2);
943 display: none;
944 "></div>
945
946 <div id="bulk-progress" style="
947 margin-top: 15px;
948 display: none;
949 ">
950 <div style="margin-bottom: 5px; font-size: 12px;">
951 Прогресс: <span id="bulk-progress-text">0/0</span>
952 </div>
953 <div style="
954 width: 100%;
955 height: 8px;
956 background: rgba(255, 255, 255, 0.3);
957 border-radius: 4px;
958 overflow: hidden;
959 ">
960 <div id="bulk-progress-bar" style="
961 width: 0%;
962 height: 100%;
963 background: white;
964 transition: width 0.3s ease;
965 "></div>
966 </div>
967 </div>
968 </div>
969 `;
970
971 document.body.appendChild(panel);
972 console.log('UI панель массового изменения создана');
973
974 // Добавляем возможность перетаскивания
975 makeDraggable(panel);
976
977 // Подсчитываем количество кампаний
978 updateCampaignsCount();
979
980 // Добавляем обработчик клика на заголовок для разворачивания/сворачивания
981 document.getElementById('bulk-panel-header').addEventListener('click', () => {
982 const content = document.getElementById('bulk-panel-content');
983 const icon = document.getElementById('bulk-toggle-icon');
984 if (content.style.display === 'none') {
985 content.style.display = 'block';
986 icon.textContent = '▲';
987 } else {
988 content.style.display = 'none';
989 icon.textContent = '▼';
990 }
991 });
992
993 // Добавляем обработчик клика на кнопку
994 document.getElementById('run-bulk-strategy-btn').addEventListener('click', runBulkStrategy);
995
996 // Добавляем обработчик для кнопки управления стратегиями
997 document.getElementById('bulk-manage-strategies-btn').addEventListener('click', async () => {
998 await createStrategyManagementModal();
999 });
1000
1001 // Добавляем обработчик для кнопки управления кластерными стратегиями
1002 document.getElementById('bulk-manage-cluster-strategies-btn').addEventListener('click', async () => {
1003 await createClusterStrategyManagementModal();
1004 });
1005
1006 // Добавляем обработчики для кнопок управления
1007 document.getElementById('pause-bulk-btn').addEventListener('click', pauseBulkProcessing);
1008 document.getElementById('stop-bulk-btn').addEventListener('click', stopBulkProcessing);
1009 }
1010
1011 // Функция для создания перетаскиваемого элемента
1012 function makeDraggable(element) {
1013 let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
1014 let isDragging = false;
1015
1016 element.onmousedown = dragMouseDown;
1017
1018 function dragMouseDown(e) {
1019 // Не перетаскиваем, если кликнули на input, button или другие интерактивные элементы
1020 if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.tagName === 'TEXTAREA') {
1021 return;
1022 }
1023
1024 e.preventDefault();
1025 isDragging = true;
1026 pos3 = e.clientX;
1027 pos4 = e.clientY;
1028 document.onmouseup = closeDragElement;
1029 document.onmousemove = elementDrag;
1030 }
1031
1032 function elementDrag(e) {
1033 if (!isDragging) return;
1034 pos1 = pos3 - e.clientX;
1035 pos2 = pos4 - e.clientY;
1036 pos3 = e.clientX;
1037 pos4 = e.clientY;
1038 element.style.top = (element.offsetTop - pos2) + 'px';
1039 element.style.left = (element.offsetLeft - pos1) + 'px';
1040 element.style.right = 'auto';
1041 element.style.bottom = 'auto';
1042 }
1043
1044 function closeDragElement() {
1045 isDragging = false;
1046 document.onmouseup = null;
1047 document.onmousemove = null;
1048 }
1049 }
1050
1051 // Функция для обновления количества кампаний
1052 function updateCampaignsCount() {
1053 const campaignLinks = document.querySelectorAll('a[href*="/campaigns/auto-campaigns/"][href*="/campaign"]');
1054 const count = campaignLinks.length;
1055 const countElement = document.getElementById('campaigns-count');
1056 if (countElement) {
1057 countElement.textContent = count;
1058 }
1059 console.log(`Найдено кампаний: ${count}`);
1060 }
1061
1062 // Функция для показа статуса массовой обработки
1063 function showBulkStatus(message, isError = false) {
1064 const statusDiv = document.getElementById('bulk-status-message');
1065 if (statusDiv) {
1066 statusDiv.textContent = message;
1067 statusDiv.style.display = 'block';
1068 statusDiv.style.background = isError ? 'rgba(255, 59, 48, 0.9)' : 'rgba(52, 199, 89, 0.9)';
1069 console.log(`Статус массовой обработки: ${message}`);
1070 }
1071 }
1072
1073 // Функция для обновления прогресса
1074 function updateBulkProgress(current, total) {
1075 const progressDiv = document.getElementById('bulk-progress');
1076 const progressText = document.getElementById('bulk-progress-text');
1077 const progressBar = document.getElementById('bulk-progress-bar');
1078
1079 if (progressDiv && progressText && progressBar) {
1080 progressDiv.style.display = 'block';
1081 progressText.textContent = `${current}/${total}`;
1082 const percentage = (current / total) * 100;
1083 progressBar.style.width = `${percentage}%`;
1084 }
1085 }
1086
1087 // Функция для прокрутки страницы вниз для загрузки всех кампаний
1088 async function scrollToLoadAllCampaigns() {
1089 console.log('Начинаем загрузку всех кампаний через прокрутку...');
1090
1091 // Находим контейнер с прокруткой
1092 const tableContainer = document.querySelector('.container.MuiBox-root.css-9hf803');
1093
1094 if (!tableContainer) {
1095 console.error('Контейнер таблицы не найден');
1096 return [];
1097 }
1098
1099 console.log('Контейнер найден, начинаем прокрутку...');
1100 console.log(`Высота контейнера: ${tableContainer.scrollHeight}px`);
1101
1102 // Собираем уникальные ссылки во время прокрутки
1103 const uniqueLinks = new Set();
1104
1105 // Прокручиваем контейнер постепенно, чтобы загрузить все кампании
1106 let previousLinksCount = 0;
1107 let stableCount = 0;
1108 const maxAttempts = 200; // Максимум попыток
1109 let attempts = 0;
1110 const scrollStep = 500; // Прокручиваем по 500px за раз
1111
1112 while (attempts < maxAttempts) {
1113 // Собираем ссылки на текущем шаге
1114 const currentLinks = document.querySelectorAll('a[href*="/campaigns/auto-campaigns/"][href*="/campaign"]');
1115 currentLinks.forEach(link => {
1116 uniqueLinks.add(link.href);
1117 });
1118
1119 const currentCount = uniqueLinks.size;
1120 console.log(`Загружено кампаний: ${currentCount}, прокрутка: ${tableContainer.scrollTop}/${tableContainer.scrollHeight}`);
1121
1122 // Прокручиваем контейнер постепенно
1123 tableContainer.scrollTop += scrollStep;
1124
1125 // Ждем загрузки новых элементов
1126 await wait(500);
1127
1128 // Если количество не изменилось
1129 if (currentCount === previousLinksCount) {
1130 stableCount++;
1131 // Если количество стабильно 5 раз подряд - значит все загружено
1132 if (stableCount >= 5) {
1133 console.log('Все кампании загружены');
1134 break;
1135 }
1136 } else {
1137 stableCount = 0;
1138 previousLinksCount = currentCount;
1139 }
1140
1141 // Если достигли конца контейнера
1142 if (tableContainer.scrollTop + tableContainer.clientHeight >= tableContainer.scrollHeight - 10) {
1143 console.log('Достигнут конец контейнера');
1144 // Ждем еще немного для загрузки последних элементов
1145 await wait(1000);
1146
1147 // Собираем последние ссылки
1148 const finalLinks = document.querySelectorAll('a[href*="/campaigns/auto-campaigns/"][href*="/campaign"]');
1149 finalLinks.forEach(link => {
1150 uniqueLinks.add(link.href);
1151 });
1152
1153 // Проверяем еще раз количество
1154 if (uniqueLinks.size === previousLinksCount) {
1155 break;
1156 }
1157 previousLinksCount = uniqueLinks.size;
1158 }
1159
1160 attempts++;
1161 }
1162
1163 // Преобразуем Set в массив URL (НЕ прокручиваем обратно!)
1164 const links = Array.from(uniqueLinks);
1165
1166 console.log(`Найдено кампаний: ${links.length}`);
1167 console.log(`Всего попыток прокрутки: ${attempts}`);
1168 return links;
1169 }
1170
1171 // Старый метод прокрутки (fallback) - удален, больше не нужен
1172
1173 // Функция для массовой обработки кампаний
1174 async function runBulkStrategy() {
1175 if (isBulkProcessing) {
1176 showBulkStatus('Обработка уже выполняется', true);
1177 return;
1178 }
1179
1180 try {
1181 isBulkProcessing = true;
1182 console.log('Начало массовой обработки кампаний');
1183
1184 bulkDesiredPercentage = parseFloat(document.getElementById('bulk-desired-percentage').value);
1185 if (!bulkDesiredPercentage || bulkDesiredPercentage <= 0) {
1186 showBulkStatus('Ошибка: введите корректный процент', true);
1187 isBulkProcessing = false;
1188 return;
1189 }
1190 console.log(`Желаемый процент: ${bulkDesiredPercentage}%`);
1191
1192 // Сохраняем ДРР для автоматической обработки
1193 await GM.setValue('bulkProcessingDRR', bulkDesiredPercentage);
1194 console.log(`ДРР ${bulkDesiredPercentage} сохранен для массовой обработки`);
1195
1196 // Прокручиваем страницу для загрузки всех кампаний
1197 const campaignLinks = await scrollToLoadAllCampaigns();
1198
1199 if (campaignLinks.length === 0) {
1200 showBulkStatus('Ошибка: кампании не найдены', true);
1201 isBulkProcessing = false;
1202 await GM.deleteValue('bulkProcessingDRR');
1203 return;
1204 }
1205
1206 console.log(`Найдено кампаний для обработки: ${campaignLinks.length}`);
1207 showBulkStatus(`Начинаем обработку ${campaignLinks.length} кампаний...`);
1208 updateBulkProgress(0, campaignLinks.length);
1209
1210 // Сохраняем список кампаний (массив URL) и текущий индекс
1211 await GM.setValue('bulkCampaigns', JSON.stringify(campaignLinks));
1212 await GM.setValue('bulkCurrentIndex', 0);
1213
1214 // Показываем кнопки управления
1215 const controlButtons = document.getElementById('bulk-control-buttons');
1216 if (controlButtons) {
1217 controlButtons.style.display = 'flex';
1218 }
1219
1220 // Открываем первую кампанию в новой вкладке
1221 if (campaignLinks.length > 0) {
1222 console.log(`Открытие кампании: ${campaignLinks[0]}`);
1223 await GM.openInTab(campaignLinks[0], false);
1224 }
1225
1226 } catch (error) {
1227 console.error('Ошибка при массовой обработке:', error);
1228 showBulkStatus(`Ошибка: ${error.message}`, true);
1229 isBulkProcessing = false;
1230 await GM.deleteValue('bulkProcessingDRR');
1231 }
1232 }
1233
1234 // Функция для паузы массовой обработки
1235 async function pauseBulkProcessing() {
1236 bulkPaused = !bulkPaused;
1237 const pauseBtn = document.getElementById('pause-bulk-btn');
1238
1239 if (bulkPaused) {
1240 await GM.setValue('bulkPaused', true);
1241 pauseBtn.textContent = '▶️ Продолжить';
1242 showBulkStatus('⏸️ Обработка приостановлена');
1243 console.log('Массовая обработка приостановлена');
1244 } else {
1245 await GM.deleteValue('bulkPaused');
1246 pauseBtn.textContent = '⏸️ Пауза';
1247 showBulkStatus('▶️ Обработка возобновлена');
1248 console.log('Массовая обработка возобновлена');
1249
1250 // Продолжаем обработку - открываем следующую кампанию
1251 const campaignsJson = await GM.getValue('bulkCampaigns', null);
1252 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
1253
1254 if (campaignsJson) {
1255 const campaigns = JSON.parse(campaignsJson);
1256 if (currentIndex < campaigns.length) {
1257 console.log(`Продолжение с кампании ${currentIndex + 1}: ${campaigns[currentIndex]}`);
1258 await GM.openInTab(campaigns[currentIndex], false);
1259 }
1260 }
1261 }
1262 }
1263
1264 // Функция для остановки массовой обработки
1265 async function stopBulkProcessing() {
1266 await GM.deleteValue('bulkProcessingDRR');
1267 await GM.deleteValue('bulkCampaigns');
1268 await GM.deleteValue('bulkCurrentIndex');
1269 await GM.deleteValue('bulkPaused');
1270
1271 showBulkStatus('⏹️ Обработка остановлена', true);
1272 console.log('Массовая обработка остановлена');
1273
1274 // Скрываем кнопки управления
1275 const controlButtons = document.getElementById('bulk-control-buttons');
1276 if (controlButtons) {
1277 controlButtons.style.display = 'none';
1278 }
1279
1280 isBulkProcessing = false;
1281 bulkPaused = false;
1282 }
1283
1284 // Функция для показа статуса
1285 function showStatus(message, isError = false) {
1286 const statusDiv = document.getElementById('status-message');
1287 if (statusDiv) {
1288 statusDiv.textContent = message;
1289 statusDiv.style.display = 'block';
1290 statusDiv.style.background = isError ? 'rgba(255, 59, 48, 0.9)' : 'rgba(52, 199, 89, 0.9)';
1291 console.log(`Статус: ${message}`);
1292 }
1293 }
1294
1295 // Функция для ожидания
1296 function wait(ms) {
1297 return new Promise(resolve => setTimeout(resolve, ms));
1298 }
1299
1300 // Функция для парсинга числа из строки
1301 function parseNumber(str) {
1302 if (!str) return 0;
1303 // Убираем все символы кроме цифр, точек и запятых
1304 const cleaned = str.replace(/[^\d.,]/g, '').replace(/\s/g, '');
1305 // Заменяем запятую на точку
1306 const normalized = cleaned.replace(',', '.');
1307 return parseFloat(normalized) || 0;
1308 }
1309
1310 // Функция для парсинга процента
1311 function parsePercentage(str) {
1312 if (!str) return 0;
1313 const cleaned = str.replace('%', '').replace(',', '.').trim();
1314 return parseFloat(cleaned) || 0;
1315 }
1316
1317 // Основная функция запуска стратегии
1318 async function runStrategy() {
1319 try {
1320 console.log('Начало выполнения стратегии');
1321 showStatus('Запуск процесса...');
1322
1323 const desiredPercentage = parseFloat(document.getElementById('desired-percentage').value);
1324 if (!desiredPercentage || desiredPercentage <= 0) {
1325 showStatus('Ошибка: введите корректный процент', true);
1326 await handleStrategyError();
1327 return;
1328 }
1329 console.log(`Желаемый процент: ${desiredPercentage}%`);
1330
1331 // Шаг 1: Кликаем на статистику
1332 showStatus('Открытие статистики...');
1333 const statsButton = document.querySelector('.css-amj7dw');
1334 if (!statsButton) {
1335 showStatus('Ошибка: кнопка статистики не найдена', true);
1336 await handleStrategyError();
1337 return;
1338 }
1339 statsButton.click();
1340 console.log('Клик на статистику выполнен');
1341 await wait(2000); // Увеличена задержка для загрузки статистики
1342
1343 // Шаг 1.5: Выбираем режим "Товары"
1344 showStatus('Выбор режима "Товары"...');
1345 const modeLabels = Array.from(document.querySelectorAll('label'));
1346 let modeInput = null;
1347
1348 for (const label of modeLabels) {
1349 if (label.textContent.trim() === 'Режим') {
1350 const inputId = label.getAttribute('for');
1351 if (inputId) {
1352 modeInput = document.getElementById(inputId);
1353 console.log(`Найдено поле Режим с id: ${inputId}`);
1354 break;
1355 }
1356 }
1357 }
1358
1359 if (modeInput) {
1360 // Устанавливаем значение "Товары" напрямую
1361 modeInput.value = 'Товары';
1362
1363 // Триггерим события для React
1364 const inputEvent = new Event('input', { bubbles: true });
1365 const changeEvent = new Event('change', { bubbles: true });
1366 modeInput.dispatchEvent(inputEvent);
1367 modeInput.dispatchEvent(changeEvent);
1368
1369 console.log('Установлено значение: Товары');
1370 await wait(1000); // Увеличена задержка для загрузки опций
1371
1372 // Проверяем, появились ли опции в выпадающем списке
1373 const options = document.querySelectorAll('[role="option"]');
1374 console.log(`Найдено опций: ${options.length}`);
1375
1376 if (options.length > 0) {
1377 // Ищем опцию "Товары" и кликаем на неё
1378 const tovarOption = Array.from(options).find(opt => opt.textContent.includes('Товары'));
1379 if (tovarOption) {
1380 tovarOption.click();
1381 console.log('Выбран режим "Товары"');
1382 await wait(1500); // Увеличена задержка для загрузки данных после выбора режима
1383 }
1384 }
1385 }
1386
1387 // Шаг 2: Извлекаем данные из статистики
1388 showStatus('Извлечение данных...');
1389
1390 // Ждем дополнительное время для загрузки статистики
1391 await wait(1000);
1392
1393 const stats = Array.from(document.querySelectorAll('.MuiTypography-caption.css-1et52kr'));
1394
1395 let sumOrders = 0;
1396 let ordersCount = 0;
1397 let cartToOrderPercent = 0;
1398
1399 stats.forEach(el => {
1400 const text = el.textContent.trim();
1401 const valueElement = el.closest('.MuiBox-root')?.querySelector('.MuiTypography-h3 .MuiTypography-body1');
1402 const value = valueElement ? valueElement.textContent.trim() : '';
1403
1404 console.log(`Найден показатель: ${text} = ${value}`);
1405
1406 if (text === 'Сумма заказов') {
1407 sumOrders = parseNumber(value);
1408 console.log(`Сумма заказов: ${sumOrders}`);
1409 } else if (text === 'Заказов') {
1410 ordersCount = parseNumber(value);
1411 console.log(`Заказов: ${ordersCount}`);
1412 } else if (text === 'Корзина → Заказ') {
1413 cartToOrderPercent = parsePercentage(value);
1414 console.log(`Корзина → Заказ: ${cartToOrderPercent}%`);
1415 }
1416 });
1417
1418 if (sumOrders === 0 || ordersCount === 0 || cartToOrderPercent === 0) {
1419 showStatus('Ошибка: не удалось получить данные статистики', true);
1420 console.error('Недостаточно данных:', { sumOrders, ordersCount, cartToOrderPercent });
1421 await handleStrategyError();
1422 return;
1423 }
1424
1425 // Шаг 3: Вычисляем стоимость корзины
1426 const cartCost = (sumOrders / ordersCount) * (desiredPercentage / 100) * (cartToOrderPercent / 100);
1427 const cartCostRounded = Math.round(cartCost * 100) / 100;
1428 console.log(`Расчет: (${sumOrders} / ${ordersCount}) * (${desiredPercentage} / 100) * (${cartToOrderPercent} / 100) = ${cartCostRounded}`);
1429 showStatus(`Рассчитано: ${cartCostRounded} ₽`);
1430
1431 // Закрываем статистику
1432 statsButton.click();
1433 await wait(500);
1434
1435 // Шаг 4: Кликаем на "Вставить стратегию"
1436 showStatus('Вставка стратегии...');
1437 const insertButtons = document.querySelectorAll('.css-5kbhos');
1438 let insertButton = null;
1439
1440 // Ищем кнопку с текстом "Вставить стратегию"
1441 for (const btn of insertButtons) {
1442 if (btn.textContent.includes('Вставить стратегию')) {
1443 insertButton = btn;
1444 break;
1445 }
1446 }
1447
1448 if (!insertButton && insertButtons.length >= 2) {
1449 insertButton = insertButtons[1];
1450 } else if (!insertButton && insertButtons.length === 1) {
1451 insertButton = insertButtons[0];
1452 }
1453
1454 if (!insertButton) {
1455 showStatus('Ошибка: кнопка "Вставить стратегию" не найдена', true);
1456 await handleStrategyError();
1457 return;
1458 }
1459
1460 // Получаем текущую выбранную стратегию
1461 const currentStrategyCode = await getCurrentStrategy();
1462 console.log('Используем стратегию, длина:', currentStrategyCode.length);
1463 console.log('Первые 50 символов стратегии:', currentStrategyCode.substring(0, 50));
1464 console.log('Последние 50 символов стратегии:', currentStrategyCode.substring(currentStrategyCode.length - 50));
1465
1466 // Копируем код стратегии в буфер обмена ПЕРЕД открытием модального окна
1467 console.log('Копируем стратегию в буфер обмена...');
1468
1469 // Пробуем несколько способов копирования
1470 try {
1471 // Способ 1: navigator.clipboard (современный)
1472 await navigator.clipboard.writeText(currentStrategyCode);
1473 console.log('✓ Стратегия скопирована через navigator.clipboard');
1474 } catch (clipboardError) {
1475 console.log('navigator.clipboard не сработал, пробуем GM.setClipboard...');
1476 // Способ 2: GM.setClipboard (fallback)
1477 await GM.setClipboard(currentStrategyCode);
1478 console.log('✓ Стратегия скопирована через GM.setClipboard');
1479 }
1480
1481 console.log('Длина стратегии:', currentStrategyCode.length);
1482 await wait(500);
1483
1484 insertButton.click();
1485 console.log('Клик на "Вставить стратегию" выполнен');
1486 await wait(1500); // Увеличена задержка для применения стратегии из буфера
1487
1488 console.log('✓ Стратегия вставлена из буфера обмена');
1489
1490 await wait(1000);
1491
1492 // Шаг 5: Находим поле "Желаемое значение" и вставляем результат
1493 showStatus('Заполнение поля...');
1494
1495 // Ищем поле с текстом "За корзину" или "Желаемое значение"
1496 const inputLabels = Array.from(document.querySelectorAll('.MuiInputLabel-root'));
1497 let targetInput = null;
1498
1499 for (const label of inputLabels) {
1500 const labelText = label.textContent;
1501 if (labelText.includes('За корзину') || labelText.includes('Желаемое значение')) {
1502 const inputId = label.getAttribute('for');
1503 if (inputId) {
1504 targetInput = document.getElementById(inputId);
1505 console.log(`Найдено поле: ${labelText}, id: ${inputId}`);
1506 break;
1507 }
1508 }
1509 }
1510
1511 // Если не нашли по лейблу, ищем по имени
1512 if (!targetInput) {
1513 const inputs = document.querySelectorAll('input[type="number"]');
1514 for (const input of inputs) {
1515 const name = input.getAttribute('name') || '';
1516 if (name.includes('rules') && name.includes('value')) {
1517 targetInput = input;
1518 console.log(`Найдено поле по имени: ${name}`);
1519 break;
1520 }
1521 }
1522 }
1523
1524 if (!targetInput) {
1525 showStatus('Ошибка: поле для ввода не найдено', true);
1526 console.error('Не удалось найти поле для ввода значения');
1527 await handleStrategyError();
1528 return;
1529 }
1530
1531 // Устанавливаем значение
1532 targetInput.focus();
1533 targetInput.value = cartCostRounded.toString();
1534
1535 // Триггерим события для React
1536 const inputEvent = new Event('input', { bubbles: true });
1537 const changeEvent = new Event('change', { bubbles: true });
1538 targetInput.dispatchEvent(inputEvent);
1539 targetInput.dispatchEvent(changeEvent);
1540
1541 console.log(`Значение ${cartCostRounded} установлено в поле ${targetInput.id}`);
1542 await wait(500);
1543
1544 // Шаг 7: Нажимаем "Сохранить"
1545 showStatus('Сохранение...');
1546 const saveButtons = document.querySelectorAll('button');
1547 let saveButton = null;
1548
1549 for (const btn of saveButtons) {
1550 if (btn.textContent.trim() === 'Сохранить') {
1551 saveButton = btn;
1552 break;
1553 }
1554 }
1555
1556 if (!saveButton) {
1557 showStatus('Ошибка: кнопка "Сохранить" не найдена', true);
1558 await handleStrategyError();
1559 return;
1560 }
1561
1562 saveButton.click();
1563 console.log('Клик на "Сохранить" выполнен');
1564 await wait(1000);
1565
1566 showStatus(`✅ Готово! Стоимость корзины: ${cartCostRounded} ₽`);
1567 console.log('Стратегия успешно установлена');
1568
1569 // Шаг 8: Работа с кластерами (только если чекбокс включен)
1570 const applyToClusters = document.getElementById('apply-to-clusters');
1571 const shouldApplyToClusters = applyToClusters ? applyToClusters.checked : true;
1572
1573 if (!shouldApplyToClusters) {
1574 console.log('Применение стратегии к кластерам отключено');
1575 showStatus('✅ Готово! Кластеры пропущены');
1576 } else {
1577 console.log('Применение стратегии к кластерам включено');
1578
1579 // 8.1: Переходим во вкладку "Кластеры"
1580 const tabs = Array.from(document.querySelectorAll('button[role="tab"]'));
1581 const clustersTab = tabs.find(tab => tab.textContent.trim() === 'Кластеры');
1582 if (!clustersTab) {
1583 console.error('Вкладка "Кластеры" не найдена');
1584 showStatus('⚠️ Вкладка "Кластеры" не найдена, пропускаем', true);
1585 } else {
1586 clustersTab.click();
1587 console.log('Клик на вкладку "Кластеры" выполнен');
1588 await wait(3000); // Увеличена задержка для загрузки кластеров
1589
1590 // Ждем, пока исчезнет скелетон загрузки
1591 let loadingAttempts = 0;
1592 while (loadingAttempts < 10) {
1593 const skeleton = document.querySelector('.MuiSkeleton-root');
1594 if (!skeleton) {
1595 console.log('Кластеры загружены');
1596 break;
1597 }
1598 console.log(`Ожидание загрузки кластеров, попытка ${loadingAttempts + 1}`);
1599 await wait(1000);
1600 loadingAttempts++;
1601 }
1602
1603 // 8.2: Выделяем все кластеры - клик в checkbox
1604 showStatus('Выделение всех кластеров...');
1605 let selectAllCheckbox = null;
1606
1607 // Способ 1: Ищем через контейнер css-1ytbthu
1608 const checkboxContainer = document.querySelector('.css-1ytbthu');
1609 if (checkboxContainer) {
1610 selectAllCheckbox = checkboxContainer.querySelector('.css-vlug8u');
1611 console.log('Найден checkbox через контейнер css-1ytbthu');
1612 }
1613
1614 // Способ 2: Ищем напрямую по классу vlug8u в заголовке таблицы
1615 if (!selectAllCheckbox) {
1616 const tableHeader = document.querySelector('thead');
1617 if (tableHeader) {
1618 selectAllCheckbox = tableHeader.querySelector('.css-vlug8u');
1619 console.log('Найден checkbox в заголовке таблицы');
1620 }
1621 }
1622
1623 // Способ 3: Ищем первый checkbox в таблице
1624 if (!selectAllCheckbox) {
1625 const checkboxes = document.querySelectorAll('input[type="checkbox"]');
1626 if (checkboxes.length > 0) {
1627 selectAllCheckbox = checkboxes[0];
1628 console.log('Найден checkbox через input[type="checkbox"]');
1629 }
1630 }
1631
1632 if (!selectAllCheckbox) {
1633 console.error('Checkbox для выделения всех кластеров не найден');
1634 showStatus('⚠️ Checkbox не найден, пропускаем кластеры', true);
1635 } else {
1636 selectAllCheckbox.click();
1637 console.log('Все кластеры выделены');
1638 await wait(1000);
1639
1640 // 8.3: Кликаем на "Действия"
1641 showStatus('Открытие меню действий...');
1642 const actionsButton = document.querySelector('.css-1rll63h');
1643 if (!actionsButton) {
1644 console.error('Кнопка "Действия" не найдена');
1645 showStatus('⚠️ Кнопка "Действия" не найдена', true);
1646 } else {
1647 actionsButton.click();
1648 console.log('Клик на "Действия" выполнен');
1649 await wait(1000);
1650
1651 // 8.4: Кликаем на "Управление" - второй элемент с классом hq58ok
1652 showStatus('Открытие управления...');
1653 const managementButtons = document.querySelectorAll('.css-hq58ok');
1654 if (managementButtons.length < 2) {
1655 console.error('Кнопка "Управление" не найдена');
1656 showStatus('⚠️ Кнопка "Управление" не найдена', true);
1657 } else {
1658 managementButtons[1].click();
1659 console.log('Клик на "Управление" выполнен');
1660 await wait(1500);
1661
1662 // 8.5: Кликаем на "Стратегия" - 3-й элемент с классом css-582wun
1663 showStatus('Открытие стратегии кластеров...');
1664 const strategyTabs = document.querySelectorAll('.css-582wun');
1665 if (strategyTabs.length < 3) {
1666 console.error('Вкладка "Стратегия" не найдена');
1667 showStatus('⚠️ Вкладка "Стратегия" не найдена', true);
1668 } else {
1669 strategyTabs[2].click();
1670 console.log('Клик на "Стратегия" выполнен');
1671 await wait(1500);
1672
1673 // 8.6: Вставляем стратегию и рассчитанную ставку
1674 showStatus('Вставка стратегии для кластеров...');
1675 const clusterInsertButtons = document.querySelectorAll('.css-5kbhos');
1676 let clusterInsertButton = null;
1677
1678 for (const btn of clusterInsertButtons) {
1679 if (btn.textContent.includes('Вставить стратегию')) {
1680 clusterInsertButton = btn;
1681 break;
1682 }
1683 }
1684
1685 if (!clusterInsertButton && clusterInsertButtons.length > 0) {
1686 clusterInsertButton = clusterInsertButtons[0];
1687 }
1688
1689 if (!clusterInsertButton) {
1690 console.error('Кнопка "Вставить стратегию" для кластеров не найдена');
1691 showStatus('⚠️ Кнопка вставки стратегии не найдена', true);
1692 } else {
1693 // Получаем текущую кластерную стратегию и копируем в буфер
1694 const currentClusterStrategyCode = await getCurrentClusterStrategy();
1695 console.log('Используем кластерную стратегию, длина:', currentClusterStrategyCode.length);
1696 console.log('Первые 50 символов кластерной стратегии:', currentClusterStrategyCode.substring(0, 50));
1697 console.log('Последние 50 символов кластерной стратегии:', currentClusterStrategyCode.substring(currentClusterStrategyCode.length - 50));
1698
1699 // Копируем код стратегии в буфер обмена
1700 console.log('Копируем кластерную стратегию в буфер обмена...');
1701
1702 // Пробуем несколько способов копирования
1703 try {
1704 // Способ 1: navigator.clipboard (современный)
1705 await navigator.clipboard.writeText(currentClusterStrategyCode);
1706 console.log('✓ Кластерная стратегия скопирована через navigator.clipboard');
1707 } catch (clipboardError) {
1708 console.log('navigator.clipboard не сработал, пробуем GM.setClipboard...');
1709 // Способ 2: GM.setClipboard (fallback)
1710 await GM.setClipboard(currentClusterStrategyCode);
1711 console.log('✓ Кластерная стратегия скопирована через GM.setClipboard');
1712 }
1713
1714 console.log('Длина кластерной стратегии:', currentClusterStrategyCode.length);
1715 await wait(500);
1716
1717 clusterInsertButton.click();
1718 console.log('Клик на "Вставить стратегию" для кластеров выполнен');
1719 await wait(1500);
1720
1721 // Находим поле для ввода ставки за корзину
1722 const clusterInputLabels = Array.from(document.querySelectorAll('.MuiInputLabel-root'));
1723 let clusterTargetInput = null;
1724
1725 for (const label of clusterInputLabels) {
1726 const labelText = label.textContent;
1727 if (labelText.includes('За корзину') || labelText.includes('Желаемое значение')) {
1728 const inputId = label.getAttribute('for');
1729 if (inputId) {
1730 clusterTargetInput = document.getElementById(inputId);
1731 console.log(`Найдено поле для кластеров: ${labelText}`);
1732 break;
1733 }
1734 }
1735 }
1736
1737 if (!clusterTargetInput) {
1738 const clusterInputs = document.querySelectorAll('input[type="number"]');
1739 for (const input of clusterInputs) {
1740 const name = input.getAttribute('name') || '';
1741 if (name.includes('CostPerAddedToCart') || name.includes('value')) {
1742 clusterTargetInput = input;
1743 console.log(`Найдено поле по имени: ${name}`);
1744 break;
1745 }
1746 }
1747 }
1748
1749 if (clusterTargetInput) {
1750 clusterTargetInput.focus();
1751 clusterTargetInput.value = cartCostRounded.toString();
1752
1753 const inputEvent = new Event('input', { bubbles: true });
1754 const changeEvent = new Event('change', { bubbles: true });
1755 clusterTargetInput.dispatchEvent(inputEvent);
1756 clusterTargetInput.dispatchEvent(changeEvent);
1757
1758 console.log(`Значение ${cartCostRounded} установлено для кластеров`);
1759 await wait(1000);
1760 }
1761
1762 // Нажимаем "Применить" после вставки стратегии - 2-й элемент с классом css-eqlbov
1763 showStatus('Применение стратегии кластеров...');
1764 const clusterStrategyApplyButtons = document.querySelectorAll('.css-eqlbov');
1765 if (clusterStrategyApplyButtons.length < 2) {
1766 console.error('Кнопка "Применить" стратегию для кластеров не найдена');
1767 showStatus('⚠️ Кнопка "Применить" стратегию не найдена', true);
1768 } else {
1769 clusterStrategyApplyButtons[1].click();
1770 console.log('Клик на "Применить" стратегию для кластеров выполнен');
1771 await wait(1500);
1772 }
1773
1774 // 8.7: Кликаем на "Автофильтры" - 3-й элемент с классом css-wi74td
1775 showStatus('Открытие автофильтров...');
1776 const autofilterButtons = document.querySelectorAll('.css-wi74td');
1777 if (autofilterButtons.length < 3) {
1778 console.error('Кнопка "Автофильтры" не найдена');
1779 showStatus('⚠️ Кнопка "Автофильтры" не найдена', true);
1780 } else {
1781 autofilterButtons[2].click();
1782 console.log('Клик на "Автофильтры" выполнен');
1783 await wait(1500);
1784
1785 // 8.8: Кликаем на "Шаблоны" - 2-й элемент с классом css-l149ws
1786 showStatus('Открытие шаблонов...');
1787 const templateButtons = document.querySelectorAll('.css-l149ws');
1788 if (templateButtons.length < 2) {
1789 console.error('Кнопка "Шаблоны" не найдена');
1790 showStatus('⚠️ Кнопка "Шаблоны" не найдена', true);
1791 } else {
1792 templateButtons[1].click();
1793 console.log('Клик на "Шаблоны" выполнен');
1794 await wait(1500);
1795
1796 // 8.9: Выбираем "Расширение" - div с data-id="Расширение" и классом css-1n9drpd
1797 showStatus('Выбор шаблона "Расширение"...');
1798
1799 // Способ 1: Ищем по data-id="Расширение"
1800 let extensionTemplate = document.querySelector('div[data-id="Расширение"]');
1801 if (extensionTemplate) {
1802 console.log('Найден шаблон по data-id="Расширение"');
1803 }
1804
1805 // Способ 2: Ищем по классу css-1n9drpd
1806 if (!extensionTemplate) {
1807 extensionTemplate = document.querySelector('.css-1n9drpd');
1808 if (extensionTemplate) {
1809 console.log('Найден шаблон по классу css-1n9drpd');
1810 }
1811 }
1812
1813 // Способ 3: Ищем по тексту "Расширение"
1814 if (!extensionTemplate) {
1815 const allElements = document.querySelectorAll('div, button, span, p');
1816 for (const el of allElements) {
1817 const text = el.textContent.trim();
1818 if (text === 'Расширение' || text.includes('Расширение')) {
1819 extensionTemplate = el;
1820 console.log(`Найден шаблон "Расширение" по тексту, элемент: ${el.tagName}, класс: ${el.className}`);
1821 break;
1822 }
1823 }
1824 }
1825
1826 if (!extensionTemplate) {
1827 console.error('Шаблон "Расширение" не найден');
1828 console.log('Доступные элементы после клика на Шаблоны:');
1829 const allVisible = document.querySelectorAll('div[data-id], div[class*="css-"]');
1830 allVisible.forEach((el, i) => {
1831 if (i < 20) { // Логируем первые 20 элементов
1832 console.log(`Элемент ${i}: data-id="${el.getAttribute('data-id')}", класс: ${el.className}, текст: ${el.textContent.substring(0, 30)}`);
1833 }
1834 });
1835 showStatus('⚠️ Шаблон "Расширение" не найден', true);
1836 } else {
1837 extensionTemplate.click();
1838 console.log(`Шаблон "Расширение" выбран, класс: ${extensionTemplate.className}`);
1839 await wait(1000);
1840 }
1841 }
1842 }
1843
1844 // 8.10: Жмем "Сохранить" - класс tn31lt
1845 showStatus('Сохранение настроек кластеров...');
1846 const clusterSaveButton = document.querySelector('.css-tn31lt');
1847 if (!clusterSaveButton) {
1848 console.error('Кнопка "Сохранить" для кластеров не найдена');
1849 showStatus('⚠️ Кнопка "Сохранить" не найдена', true);
1850 } else {
1851 clusterSaveButton.click();
1852 console.log('Клик на "Сохранить" для кластеров выполнен');
1853 await wait(2000);
1854 showStatus('✅ Кластеры настроены!');
1855 }
1856 }
1857 }
1858 }
1859 }
1860 }
1861 }
1862 }
1863
1864 showStatus(`✅ Полностью готово! Стоимость корзины: ${cartCostRounded} ₽`);
1865 console.log('Стратегия и кластеры успешно настроены');
1866
1867 // Проверяем, есть ли массовая обработка
1868 const savedDRR = await GM.getValue('bulkProcessingDRR', null);
1869 if (savedDRR !== null) {
1870 console.log('Обнаружена массовая обработка, переходим к следующей кампании');
1871
1872 // Проверяем, не на паузе ли обработка
1873 const isPaused = await GM.getValue('bulkPaused', false);
1874 if (isPaused) {
1875 console.log('Обработка на паузе, ждем возобновления');
1876 showStatus('⏸️ Обработка на паузе');
1877 return;
1878 }
1879
1880 // Получаем список кампаний и текущий индекс
1881 const campaignsJson = await GM.getValue('bulkCampaigns', null);
1882 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
1883
1884 if (campaignsJson) {
1885 const campaigns = JSON.parse(campaignsJson);
1886 const nextIndex = currentIndex + 1;
1887
1888 console.log(`Обработано ${nextIndex} из ${campaigns.length} кампаний`);
1889
1890 if (nextIndex < campaigns.length) {
1891 // Сохраняем новый индекс
1892 await GM.setValue('bulkCurrentIndex', nextIndex);
1893
1894 // Открываем следующую кампанию в новой вкладке
1895 console.log(`Открытие кампании ${nextIndex + 1}: ${campaigns[nextIndex]}`);
1896 await GM.openInTab(campaigns[nextIndex], false);
1897
1898 // Закрываем текущую вкладку
1899 await wait(1000);
1900 window.close();
1901 } else {
1902 // Все кампании обработаны
1903 console.log('Все кампании обработаны, очищаем данные');
1904 await GM.deleteValue('bulkProcessingDRR');
1905 await GM.deleteValue('bulkCampaigns');
1906 await GM.deleteValue('bulkCurrentIndex');
1907
1908 // Закрываем текущую вкладку
1909 showStatus('✅ Все кампании обработаны! Закрытие вкладки...');
1910 await wait(2000);
1911 window.close();
1912 }
1913 }
1914 }
1915
1916 } catch (error) {
1917 console.error('Ошибка при выполнении стратегии:', error);
1918 showStatus(`Ошибка: ${error.message}`, true);
1919 await handleStrategyError();
1920 }
1921 }
1922
1923 // Функция для обработки ошибок и перехода к следующей кампании
1924 async function handleStrategyError() {
1925 const savedDRR = await GM.getValue('bulkProcessingDRR', null);
1926 if (savedDRR !== null) {
1927 console.log('Обнаружена ошибка при массовой обработке, ждем 10 секунд перед переходом к следующей кампании');
1928 showStatus('⚠️ Ошибка! Переход к следующей кампании через 10 секунд...', true);
1929
1930 // Ждем 10 секунд
1931 for (let i = 10; i > 0; i--) {
1932 showStatus(`⚠️ Ошибка! Переход к следующей через ${i} сек...`, true);
1933 await wait(1000);
1934 }
1935
1936 // Получаем список кампаний и текущий индекс
1937 const campaignsJson = await GM.getValue('bulkCampaigns', null);
1938 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
1939
1940 if (campaignsJson) {
1941 const campaigns = JSON.parse(campaignsJson);
1942 const nextIndex = currentIndex + 1;
1943
1944 console.log(`Пропускаем кампанию с ошибкой. Переход к ${nextIndex + 1} из ${campaigns.length}`);
1945
1946 if (nextIndex < campaigns.length) {
1947 // Сохраняем новый индекс
1948 await GM.setValue('bulkCurrentIndex', nextIndex);
1949
1950 // Открываем следующую кампанию в новой вкладке
1951 console.log(`Открытие кампании ${nextIndex + 1}: ${campaigns[nextIndex]}`);
1952 await GM.openInTab(campaigns[nextIndex], false);
1953
1954 // Закрываем текущую вкладку
1955 await wait(1000);
1956 window.close();
1957 } else {
1958 // Все кампании обработаны
1959 console.log('Все кампании обработаны, очищаем данные');
1960 await GM.deleteValue('bulkProcessingDRR');
1961 await GM.deleteValue('bulkCampaigns');
1962 await GM.deleteValue('bulkCurrentIndex');
1963
1964 // Закрываем текущую вкладку
1965 showStatus('✅ Все кампании обработаны! Закрытие вкладки...');
1966 await wait(2000);
1967 window.close();
1968 }
1969 }
1970 }
1971 }
1972
1973 // Инициализация
1974 function init() {
1975 console.log('Инициализация расширения');
1976
1977 // Проверяем, на какой странице мы находимся
1978 if (window.location.href.includes('/advert/campaigns')) {
1979 // Страница со списком кампаний
1980 console.log('Страница списка кампаний обнаружена');
1981
1982 setTimeout(() => {
1983 if (document.body) {
1984 createBulkUI();
1985 } else {
1986 console.error('Body не найден');
1987 }
1988 }, 1000);
1989 } else if (window.location.href.includes('/campaigns/auto-campaigns/') && window.location.href.includes('/campaign')) {
1990 // Страница отдельной кампании
1991 console.log('Страница кампании обнаружена');
1992
1993 // Создаем UI с небольшой задержкой, чтобы убедиться, что страница загружена
1994 setTimeout(() => {
1995 if (document.body) {
1996 createUI();
1997 } else {
1998 console.error('Body не найден');
1999 }
2000 }, 1000);
2001 } else {
2002 console.log('Не на странице кампании, UI не создается');
2003 }
2004 }
2005
2006 // Запускаем инициализацию
2007 init();
2008
2009})();