Автоматическая установка стратегии рекламной кампании на основе статистики
Size
110.8 KB
Version
1.8.33
Created
Feb 12, 2026
Updated
about 1 month ago
1// ==UserScript==
2// @name MP Manager Cluster Auto Strategy Setter
3// @description Автоматическая установка стратегии рекламной кампании на основе статистики
4// @version 1.8.33
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 = 30;
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="30"
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="30"
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 await GM.setValue('bulkTotalCampaigns', campaignLinks.length);
1214
1215 // Показываем кнопки управления
1216 const controlButtons = document.getElementById('bulk-control-buttons');
1217 if (controlButtons) {
1218 controlButtons.style.display = 'flex';
1219 }
1220
1221 // Открываем первую кампанию в новой вкладке через window.open
1222 if (campaignLinks.length > 0) {
1223 console.log(`Открытие кампании 1/${campaignLinks.length}: ${campaignLinks[0]}`);
1224 window.open(campaignLinks[0], '_blank');
1225 }
1226
1227 } catch (error) {
1228 console.error('Ошибка при массовой обработке:', error);
1229 showBulkStatus(`Ошибка: ${error.message}`, true);
1230 isBulkProcessing = false;
1231 await GM.deleteValue('bulkProcessingDRR');
1232 }
1233 }
1234
1235 // Функция для паузы массовой обработки
1236 async function pauseBulkProcessing() {
1237 bulkPaused = !bulkPaused;
1238 const pauseBtn = document.getElementById('pause-bulk-btn');
1239
1240 if (bulkPaused) {
1241 await GM.setValue('bulkPaused', true);
1242 pauseBtn.textContent = '▶️ Продолжить';
1243 showBulkStatus('⏸️ Обработка приостановлена');
1244 console.log('Массовая обработка приостановлена');
1245 } else {
1246 await GM.deleteValue('bulkPaused');
1247 pauseBtn.textContent = '⏸️ Пауза';
1248 showBulkStatus('▶️ Обработка возобновлена');
1249 console.log('Массовая обработка возобновлена');
1250
1251 // Продолжаем обработку - открываем следующую кампанию
1252 const campaignsJson = await GM.getValue('bulkCampaigns', null);
1253 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
1254 const totalCampaigns = await GM.getValue('bulkTotalCampaigns', 0);
1255
1256 if (campaignsJson) {
1257 const campaigns = JSON.parse(campaignsJson);
1258 const nextIndex = currentIndex + 1;
1259
1260 console.log(`Обработано кампаний: ${nextIndex} из ${totalCampaigns}`);
1261
1262 if (nextIndex < campaigns.length) {
1263 // Сохраняем новый индекс
1264 await GM.setValue('bulkCurrentIndex', nextIndex);
1265
1266 // Открываем следующую кампанию в новой вкладке через window.open
1267 console.log(`Открываем кампанию ${nextIndex + 1}/${totalCampaigns}...`);
1268 const newTab = window.open(campaigns[nextIndex], '_blank');
1269
1270 if (newTab) {
1271 // Закрываем текущую вкладку
1272 await wait(1000);
1273 window.close();
1274 } else {
1275 // Если блокировщик всплывающих окон, используем редирект
1276 window.location.href = campaigns[nextIndex];
1277 }
1278 }
1279 }
1280 }
1281 }
1282
1283 // Функция для остановки массовой обработки
1284 async function stopBulkProcessing() {
1285 await GM.deleteValue('bulkProcessingDRR');
1286 await GM.deleteValue('bulkCampaigns');
1287 await GM.deleteValue('bulkCurrentIndex');
1288 await GM.deleteValue('bulkPaused');
1289
1290 showBulkStatus('⏹️ Обработка остановлена', true);
1291 console.log('Массовая обработка остановлена');
1292
1293 // Скрываем кнопки управления
1294 const controlButtons = document.getElementById('bulk-control-buttons');
1295 if (controlButtons) {
1296 controlButtons.style.display = 'none';
1297 }
1298
1299 isBulkProcessing = false;
1300 bulkPaused = false;
1301 }
1302
1303 // Функция для показа статуса
1304 function showStatus(message, isError = false) {
1305 const statusDiv = document.getElementById('status-message');
1306 if (statusDiv) {
1307 statusDiv.textContent = message;
1308 statusDiv.style.display = 'block';
1309 statusDiv.style.background = isError ? 'rgba(255, 59, 48, 0.9)' : 'rgba(52, 199, 89, 0.9)';
1310 console.log(`Статус: ${message}`);
1311 }
1312 }
1313
1314 // Функция для ожидания
1315 function wait(ms) {
1316 return new Promise(resolve => setTimeout(resolve, ms));
1317 }
1318
1319 // Функция для парсинга числа из строки
1320 function parseNumber(str) {
1321 if (!str) return 0;
1322 // Убираем все символы кроме цифр, точек и запятых
1323 const cleaned = str.replace(/[^\d.,]/g, '').replace(/\s/g, '');
1324 // Заменяем запятую на точку
1325 const normalized = cleaned.replace(',', '.');
1326 return parseFloat(normalized) || 0;
1327 }
1328
1329 // Функция для парсинга процента
1330 function parsePercentage(str) {
1331 if (!str) return 0;
1332 const cleaned = str.replace('%', '').replace(',', '.').trim();
1333 return parseFloat(cleaned) || 0;
1334 }
1335
1336 // Основная функция запуска стратегии
1337 async function runStrategy() {
1338 try {
1339 console.log('Начало выполнения стратегии');
1340 showStatus('Запуск процесса...');
1341
1342 const desiredPercentage = parseFloat(document.getElementById('desired-percentage').value);
1343 if (!desiredPercentage || desiredPercentage <= 0) {
1344 showStatus('Ошибка: введите корректный процент', true);
1345 await handleStrategyError();
1346 return;
1347 }
1348 console.log(`Желаемый процент: ${desiredPercentage}%`);
1349
1350 // Шаг 1: Кликаем на статистику
1351 showStatus('Открытие статистики...');
1352 const statsButton = document.querySelector('.css-amj7dw');
1353 if (!statsButton) {
1354 showStatus('Ошибка: кнопка статистики не найдена', true);
1355 await handleStrategyError();
1356 return;
1357 }
1358 statsButton.click();
1359 console.log('Клик на статистику выполнен');
1360 await wait(2000); // Увеличена задержка для загрузки статистики
1361
1362 // Шаг 1.5: Выбираем режим "Товары"
1363 showStatus('Выбор режима "Товары"...');
1364 const modeLabels = Array.from(document.querySelectorAll('label'));
1365 let modeInput = null;
1366
1367 for (const label of modeLabels) {
1368 if (label.textContent.trim() === 'Режим') {
1369 const inputId = label.getAttribute('for');
1370 if (inputId) {
1371 modeInput = document.getElementById(inputId);
1372 console.log(`Найдено поле Режим с id: ${inputId}`);
1373 break;
1374 }
1375 }
1376 }
1377
1378 if (modeInput) {
1379 // Устанавливаем значение "Товары" напрямую
1380 modeInput.value = 'Товары';
1381
1382 // Триггерим события для React
1383 const inputEvent = new Event('input', { bubbles: true });
1384 const changeEvent = new Event('change', { bubbles: true });
1385 modeInput.dispatchEvent(inputEvent);
1386 modeInput.dispatchEvent(changeEvent);
1387
1388 console.log('Установлено значение: Товары');
1389 await wait(1000); // Увеличена задержка для загрузки опций
1390
1391 // Проверяем, появились ли опции в выпадающем списке
1392 const options = document.querySelectorAll('[role="option"]');
1393 console.log(`Найдено опций: ${options.length}`);
1394
1395 if (options.length > 0) {
1396 // Ищем опцию "Товары" и кликаем на неё
1397 const tovarOption = Array.from(options).find(opt => opt.textContent.includes('Товары'));
1398 if (tovarOption) {
1399 tovarOption.click();
1400 console.log('Выбран режим "Товары"');
1401 await wait(1000); // Увеличена задержка для загрузки данных после выбора режима
1402 }
1403 }
1404 }
1405
1406 // Шаг 2: Извлекаем данные из статистики
1407 showStatus('Извлечение данных...');
1408
1409 // Ждем дополнительное время для загрузки статистики
1410 await wait(500);
1411
1412 const stats = Array.from(document.querySelectorAll('.MuiTypography-caption.css-1et52kr'));
1413
1414 let sumOrders = 0;
1415 let ordersCount = 0;
1416 let cartToOrderPercent = 0;
1417
1418 stats.forEach(el => {
1419 const text = el.textContent.trim();
1420 const valueElement = el.closest('.MuiBox-root')?.querySelector('.MuiTypography-h3 .MuiTypography-body1');
1421 const value = valueElement ? valueElement.textContent.trim() : '';
1422
1423 console.log(`Найден показатель: ${text} = ${value}`);
1424
1425 if (text === 'Сумма заказов') {
1426 sumOrders = parseNumber(value);
1427 console.log(`Сумма заказов: ${sumOrders}`);
1428 } else if (text === 'Заказов') {
1429 ordersCount = parseNumber(value);
1430 console.log(`Заказов: ${ordersCount}`);
1431 } else if (text === 'Корзина → Заказ') {
1432 cartToOrderPercent = parsePercentage(value);
1433 console.log(`Корзина → Заказ: ${cartToOrderPercent}%`);
1434 }
1435 });
1436
1437 if (sumOrders === 0 || ordersCount === 0 || cartToOrderPercent === 0) {
1438 showStatus('Ошибка: не удалось получить данные статистики', true);
1439 console.error('Недостаточно данных:', { sumOrders, ordersCount, cartToOrderPercent });
1440 await handleStrategyError();
1441 return;
1442 }
1443
1444 // Шаг 3: Вычисляем стоимость корзины
1445 const cartCost = (sumOrders / ordersCount) * (desiredPercentage / 100) * (cartToOrderPercent / 100);
1446 const cartCostRounded = Math.round(cartCost * 100) / 100;
1447 console.log(`Расчет: (${sumOrders} / ${ordersCount}) * (${desiredPercentage} / 100) * (${cartToOrderPercent} / 100) = ${cartCostRounded}`);
1448 showStatus(`Рассчитано: ${cartCostRounded} ₽`);
1449
1450 // Закрываем статистику
1451 statsButton.click();
1452 await wait(500);
1453
1454 // Шаг 4: Кликаем на "Вставить стратегию"
1455 showStatus('Вставка стратегии...');
1456 const insertButtons = document.querySelectorAll('.css-5kbhos');
1457 let insertButton = null;
1458
1459 // Ищем кнопку с текстом "Вставить стратегию"
1460 for (const btn of insertButtons) {
1461 if (btn.textContent.includes('Вставить стратегию')) {
1462 insertButton = btn;
1463 break;
1464 }
1465 }
1466
1467 if (!insertButton && insertButtons.length >= 2) {
1468 insertButton = insertButtons[1];
1469 } else if (!insertButton && insertButtons.length === 1) {
1470 insertButton = insertButtons[0];
1471 }
1472
1473 if (!insertButton) {
1474 showStatus('Ошибка: кнопка "Вставить стратегию" не найдена', true);
1475 await handleStrategyError();
1476 return;
1477 }
1478
1479 // Получаем текущую выбранную стратегию
1480 const currentStrategyCode = await getCurrentStrategy();
1481 console.log('Используем стратегию, длина:', currentStrategyCode.length);
1482 console.log('Первые 50 символов стратегии:', currentStrategyCode.substring(0, 50));
1483 console.log('Последние 50 символов стратегии:', currentStrategyCode.substring(currentStrategyCode.length - 50));
1484
1485 // Копируем код стратегии в буфер обмена ПЕРЕД открытием модального окна
1486 console.log('Копируем стратегию в буфер обмена...');
1487
1488 // Пробуем несколько способов копирования
1489 try {
1490 // Способ 1: navigator.clipboard (современный)
1491 await navigator.clipboard.writeText(currentStrategyCode);
1492 console.log('✓ Стратегия скопирована через navigator.clipboard');
1493 } catch (e) {
1494 console.log('navigator.clipboard не сработал:', e.message);
1495 console.log('Пробуем GM.setClipboard...');
1496 // Способ 2: GM.setClipboard (fallback)
1497 try {
1498 await GM.setClipboard(currentStrategyCode);
1499 console.log('✓ Стратегия скопирована через GM.setClipboard');
1500 } catch (e2) {
1501 console.error('Ошибка копирования через GM.setClipboard:', e2.message);
1502 }
1503 }
1504
1505 console.log('Длина стратегии:', currentStrategyCode.length);
1506 await wait(500);
1507
1508 insertButton.click();
1509 console.log('Клик на "Вставить стратегию" выполнен');
1510 await wait(500); // Увеличена задержка для применения стратегии из буфера
1511
1512 console.log('✓ Стратегия вставлена из буфера обмена');
1513
1514 await wait(500);
1515
1516 // Шаг 5: Находим поле "Желаемое значение" и вставляем результат
1517 showStatus('Заполнение поля...');
1518
1519 // Ищем поле с текстом "За корзину" или "Желаемое значение"
1520 const inputLabels = Array.from(document.querySelectorAll('.MuiInputLabel-root'));
1521 let targetInput = null;
1522
1523 for (const label of inputLabels) {
1524 const labelText = label.textContent;
1525 if (labelText.includes('Желаемое значение За корзину')) {
1526 const inputId = label.getAttribute('for');
1527 if (inputId) {
1528 targetInput = document.getElementById(inputId);
1529 console.log(`Найдено поле: ${labelText}, id: ${inputId}`);
1530 break;
1531 }
1532 }
1533 }
1534
1535 // Если не нашли по лейблу, ищем по имени
1536 if (!targetInput) {
1537 const inputs = document.querySelectorAll('input[type="number"]');
1538 for (const input of inputs) {
1539 const name = input.getAttribute('name') || '';
1540 if (name.includes('rules') && name.includes('value')) {
1541 targetInput = input;
1542 console.log(`Найдено поле по имени: ${name}`);
1543 break;
1544 }
1545 }
1546 }
1547
1548 if (!targetInput) {
1549 showStatus('Ошибка: поле для ввода не найдено', true);
1550 console.error('Не удалось найти поле для ввода значения');
1551 await handleStrategyError();
1552 return;
1553 }
1554
1555 // Устанавливаем значение
1556 targetInput.focus();
1557 targetInput.value = cartCostRounded.toString();
1558
1559 // Триггерим события для React
1560 const inputEvent = new Event('input', { bubbles: true });
1561 const changeEvent = new Event('change', { bubbles: true });
1562 targetInput.dispatchEvent(inputEvent);
1563 targetInput.dispatchEvent(changeEvent);
1564
1565 console.log(`Значение ${cartCostRounded} установлено в поле ${targetInput.id}`);
1566 await wait(500);
1567
1568 // Шаг 7: Нажимаем "Сохранить"
1569 showStatus('Сохранение...');
1570 const saveButtons = document.querySelectorAll('button');
1571 let saveButton = null;
1572
1573 for (const btn of saveButtons) {
1574 if (btn.textContent.trim() === 'Сохранить') {
1575 saveButton = btn;
1576 break;
1577 }
1578 }
1579
1580 if (!saveButton) {
1581 showStatus('Ошибка: кнопка "Сохранить" не найдена', true);
1582 await handleStrategyError();
1583 return;
1584 }
1585
1586 saveButton.click();
1587 console.log('Клик на "Сохранить" выполнен');
1588 await wait(500);
1589
1590 showStatus(`✅ Готово! Стоимость корзины: ${cartCostRounded} ₽`);
1591 console.log('Стратегия успешно установлена');
1592
1593 // Шаг 8: Работа с кластерами (только если чекбокс включен)
1594 const applyToClusters = document.getElementById('apply-to-clusters');
1595 const shouldApplyToClusters = applyToClusters ? applyToClusters.checked : true;
1596
1597 if (!shouldApplyToClusters) {
1598 console.log('Применение стратегии к кластерам отключено');
1599 showStatus('✅ Готово! Кластеры пропущены');
1600 } else {
1601 console.log('Применение стратегии к кластерам включено');
1602
1603 // 8.1: Переходим во вкладку "Кластеры"
1604 const tabs = Array.from(document.querySelectorAll('button[role="tab"]'));
1605 const clustersTab = tabs.find(tab => tab.textContent.trim() === 'Кластеры');
1606 if (!clustersTab) {
1607 console.error('Вкладка "Кластеры" не найдена');
1608 showStatus('⚠️ Вкладка "Кластеры" не найдена, пропускаем', true);
1609 } else {
1610 clustersTab.click();
1611 console.log('Клик на вкладку "Кластеры" выполнен');
1612 await wait(1000); // Увеличена задержка для загрузки кластеров
1613
1614 // Ждем, пока исчезнет скелетон загрузки
1615 let loadingAttempts = 0;
1616 while (loadingAttempts < 10) {
1617 const skeleton = document.querySelector('.MuiSkeleton-root');
1618 if (!skeleton) {
1619 console.log('Кластеры загружены');
1620 break;
1621 }
1622 console.log(`Ожидание загрузки кластеров, попытка ${loadingAttempts + 1}`);
1623 await wait(500);
1624 loadingAttempts++;
1625 }
1626
1627 // 8.2: Выделяем все кластеры - клик в checkbox
1628 showStatus('Выделение всех кластеров...');
1629 let selectAllCheckbox = null;
1630
1631 // Способ 1: Ищем через контейнер css-1ytbthu
1632 const checkboxContainer = document.querySelector('.css-1ytbthu');
1633 if (checkboxContainer) {
1634 selectAllCheckbox = checkboxContainer.querySelector('.css-vlug8u');
1635 console.log('Найден checkbox через контейнер css-1ytbthu');
1636 }
1637
1638 // Способ 2: Ищем напрямую по классу vlug8u в заголовке таблицы
1639 if (!selectAllCheckbox) {
1640 const tableHeader = document.querySelector('thead');
1641 if (tableHeader) {
1642 selectAllCheckbox = tableHeader.querySelector('.css-vlug8u');
1643 console.log('Найден checkbox в заголовке таблицы');
1644 }
1645 }
1646
1647 // Способ 3: Ищем первый checkbox в таблице
1648 if (!selectAllCheckbox) {
1649 const checkboxes = document.querySelectorAll('input[type="checkbox"]');
1650 if (checkboxes.length > 0) {
1651 selectAllCheckbox = checkboxes[0];
1652 console.log('Найден checkbox через input[type="checkbox"]');
1653 }
1654 }
1655
1656 if (!selectAllCheckbox) {
1657 console.error('Checkbox для выделения всех кластеров не найден');
1658 showStatus('⚠️ Checkbox не найден, пропускаем кластеры', true);
1659 } else {
1660 selectAllCheckbox.click();
1661 console.log('Все кластеры выделены');
1662 await wait(500);
1663
1664 // 8.3: Кликаем на "Действия"
1665 showStatus('Открытие меню действий...');
1666 const actionsButton = document.querySelector('.css-1rll63h');
1667 if (!actionsButton) {
1668 console.error('Кнопка "Действия" не найдена');
1669 showStatus('⚠️ Кнопка "Действия" не найдена', true);
1670 } else {
1671 actionsButton.click();
1672 console.log('Клик на "Действия" выполнен');
1673 await wait(500);
1674
1675 // 8.4: Кликаем на "Управление" - второй элемент с классом hq58ok
1676 showStatus('Открытие управления...');
1677 const managementButtons = document.querySelectorAll('.css-hq58ok');
1678 if (managementButtons.length < 2) {
1679 console.error('Кнопка "Управление" не найдена');
1680 showStatus('⚠️ Кнопка "Управление" не найдена', true);
1681 } else {
1682 managementButtons[1].click();
1683 console.log('Клик на "Управление" выполнен');
1684 await wait(500);
1685
1686 // 8.5: Кликаем на "Стратегия" - 3-й элемент с классом css-582wun
1687 showStatus('Открытие стратегии кластеров...');
1688 const strategyTabs = document.querySelectorAll('.css-582wun');
1689 if (strategyTabs.length < 3) {
1690 console.error('Вкладка "Стратегия" не найдена');
1691 showStatus('⚠️ Вкладка "Стратегия" не найдена', true);
1692 } else {
1693 strategyTabs[2].click();
1694 console.log('Клик на "Стратегия" выполнен');
1695 await wait(500);
1696
1697 // 8.6: Вставляем стратегию и рассчитанную ставку
1698 showStatus('Вставка стратегии для кластеров...');
1699 const clusterInsertButtons = document.querySelectorAll('.css-5kbhos');
1700 let clusterInsertButton = null;
1701
1702 for (const btn of clusterInsertButtons) {
1703 if (btn.textContent.includes('Вставить стратегию')) {
1704 clusterInsertButton = btn;
1705 break;
1706 }
1707 }
1708
1709 if (!clusterInsertButton && clusterInsertButtons.length > 0) {
1710 clusterInsertButton = clusterInsertButtons[0];
1711 }
1712
1713 if (!clusterInsertButton) {
1714 console.error('Кнопка "Вставить стратегию" для кластеров не найдена');
1715 showStatus('⚠️ Кнопка вставки стратегии не найдена', true);
1716 } else {
1717 // Получаем текущую кластерную стратегию и копируем в буфер
1718 const currentClusterStrategyCode = await getCurrentClusterStrategy();
1719 console.log('Используем кластерную стратегию, длина:', currentClusterStrategyCode.length);
1720 console.log('Первые 50 символов кластерной стратегии:', currentClusterStrategyCode.substring(0, 50));
1721 console.log('Последние 50 символов кластерной стратегии:', currentClusterStrategyCode.substring(currentClusterStrategyCode.length - 50));
1722
1723 // Копируем код стратегии в буфер обмена
1724 console.log('Копируем кластерную стратегию в буфер обмена...');
1725
1726 // Пробуем несколько способов копирования
1727 try {
1728 // Способ 1: navigator.clipboard (современный)
1729 await navigator.clipboard.writeText(currentClusterStrategyCode);
1730 console.log('✓ Кластерная стратегия скопирована через navigator.clipboard');
1731 } catch (e) {
1732 console.log('navigator.clipboard не сработал, пробуем GM.setClipboard...');
1733 // Способ 2: GM.setClipboard (fallback)
1734 await GM.setClipboard(currentClusterStrategyCode);
1735 console.log('✓ Кластерная стратегия скопирована через GM.setClipboard');
1736 }
1737
1738 console.log('Длина кластерной стратегии:', currentClusterStrategyCode.length);
1739 await wait(500);
1740
1741 clusterInsertButton.click();
1742 console.log('Клик на "Вставить стратегию" для кластеров выполнен');
1743 await wait(1000);
1744
1745 // Находим поле для ввода ставки за корзину
1746 const clusterInputLabels = Array.from(document.querySelectorAll('.MuiInputLabel-root'));
1747 let clusterTargetInput = null;
1748
1749 for (const label of clusterInputLabels) {
1750 const labelText = label.textContent;
1751 if (labelText.includes('Желаемое значение За корзину')) {
1752 const inputId = label.getAttribute('for');
1753 if (inputId) {
1754 clusterTargetInput = document.getElementById(inputId);
1755 console.log(`Найдено поле для кластеров: ${labelText}`);
1756 break;
1757 }
1758 }
1759 }
1760
1761 if (!clusterTargetInput) {
1762 const clusterInputs = document.querySelectorAll('input[type="number"]');
1763 for (const input of clusterInputs) {
1764 const name = input.getAttribute('name') || '';
1765 if (name.includes('CostPerAddedToCart') || name.includes('value')) {
1766 clusterTargetInput = input;
1767 console.log(`Найдено поле по имени: ${name}`);
1768 break;
1769 }
1770 }
1771 }
1772
1773 if (clusterTargetInput) {
1774 clusterTargetInput.focus();
1775 clusterTargetInput.value = cartCostRounded.toString();
1776
1777 const inputEvent = new Event('input', { bubbles: true });
1778 const changeEvent = new Event('change', { bubbles: true });
1779 clusterTargetInput.dispatchEvent(inputEvent);
1780 clusterTargetInput.dispatchEvent(changeEvent);
1781
1782 console.log(`Значение ${cartCostRounded} установлено для кластеров`);
1783 await wait(500);
1784 }
1785
1786 // Нажимаем "Применить" после вставки стратегии - 2-й элемент с классом css-eqlbov
1787 showStatus('Применение стратегии кластеров...');
1788 const clusterStrategyApplyButtons = document.querySelectorAll('.css-eqlbov');
1789 if (clusterStrategyApplyButtons.length < 2) {
1790 console.error('Кнопка "Применить" стратегию для кластеров не найдена');
1791 showStatus('⚠️ Кнопка "Применить" стратегию не найдена', true);
1792 } else {
1793 clusterStrategyApplyButtons[1].click();
1794 console.log('Клик на "Применить" стратегию для кластеров выполнен');
1795 await wait(1000);
1796
1797 // 8.7: Закрываем модальное окно управления кластерами (если оно открыто)
1798 // Ищем кнопку закрытия модального окна
1799 const closeModalButtons = document.querySelectorAll('button[aria-label="Close"]');
1800 if (closeModalButtons.length > 0) {
1801 closeModalButtons[closeModalButtons.length - 1].click();
1802 console.log('Модальное окно управления закрыто');
1803 await wait(1000);
1804 }
1805
1806 // 8.8: Работаем с аккордеоном "Автофильтры" на странице кластеров
1807 showStatus('Открытие автофильтров...');
1808
1809 // Ищем аккордеон "Автофильтры" по ID
1810 const autofilterAccordion = document.getElementById('bidder-constructor');
1811 if (!autofilterAccordion) {
1812 console.error('Аккордеон "Автофильтры" не найден');
1813 showStatus('⚠️ Аккордеон "Автофильтры" не найден', true);
1814 } else {
1815 // Проверяем, раскрыт ли аккордеон
1816 const accordionButton = autofilterAccordion.querySelector('button[aria-expanded]');
1817 const isExpanded = accordionButton && accordionButton.getAttribute('aria-expanded') === 'true';
1818
1819 if (!isExpanded && accordionButton) {
1820 // Раскрываем аккордеон
1821 accordionButton.click();
1822 console.log('Аккордеон "Автофильтры" раскрыт');
1823 await wait(1000);
1824 } else {
1825 console.log('Аккордеон "Автофильтры" уже раскрыт');
1826 }
1827
1828 // 8.9: Кликаем на "Шаблоны" внутри аккордеона
1829 showStatus('Открытие шаблонов...');
1830
1831 // Ищем кнопку "Шаблоны" внутри аккордеона
1832 const templatesButtons = autofilterAccordion.querySelectorAll('button');
1833 let templateButton = null;
1834
1835 for (const btn of templatesButtons) {
1836 if (btn.textContent.trim() === 'Шаблоны') {
1837 templateButton = btn;
1838 console.log('Найдена кнопка "Шаблоны" в аккордеоне');
1839 break;
1840 }
1841 }
1842
1843 if (!templateButton) {
1844 console.error('Кнопка "Шаблоны" не найдена в аккордеоне');
1845 showStatus('⚠️ Кнопка "Шаблоны" не найдена', true);
1846 } else {
1847 templateButton.click();
1848 console.log('Клик на "Шаблоны" выполнен');
1849 await wait(500);
1850
1851 // 8.10: Выбираем "Расширение"
1852 showStatus('Выбор шаблона "Расширение"...');
1853
1854 // Ищем элемент с текстом "Расширение"
1855 const allElements = document.querySelectorAll('div, button, span, p');
1856 let extensionTemplate = null;
1857
1858 for (const el of allElements) {
1859 const text = el.textContent.trim();
1860 if (text === 'Расширение') {
1861 extensionTemplate = el;
1862 console.log(`Найден шаблон "Расширение", элемент: ${el.tagName}, класс: ${el.className}`);
1863 break;
1864 }
1865 }
1866
1867 if (!extensionTemplate) {
1868 console.error('Шаблон "Расширение" не найден');
1869 console.log('Доступные элементы после клика на Шаблоны:');
1870 const allVisible = document.querySelectorAll('div, button, span, p');
1871 allVisible.forEach((el, i) => {
1872 if (i < 20) {
1873 console.log(`Элемент ${i}: ${el.tagName}, текст: ${el.textContent.substring(0, 30)}`);
1874 }
1875 });
1876 showStatus('⚠️ Шаблон "Расширение" не найден', true);
1877 } else {
1878 extensionTemplate.click();
1879 console.log('Шаблон "Расширение" выбран');
1880 await wait(500);
1881
1882 // 8.11: Кликаем на "Сохранить"
1883 showStatus('Сохранение автофильтров...');
1884
1885 const saveButtonsAfterTemplate = document.querySelectorAll('button');
1886 let saveAutofilterButton = null;
1887
1888 for (const btn of saveButtonsAfterTemplate) {
1889 if (btn.textContent.trim() === 'Сохранить') {
1890 saveAutofilterButton = btn;
1891 console.log('Найдена кнопка "Сохранить" для автофильтров');
1892 break;
1893 }
1894 }
1895
1896 if (!saveAutofilterButton) {
1897 console.error('Кнопка "Сохранить" для автофильтров не найдена');
1898 showStatus('⚠️ Кнопка "Сохранить" не найдена', true);
1899 } else {
1900 saveAutofilterButton.click();
1901 console.log('Клик на "Сохранить" автофильтры выполнен');
1902 await wait(1000);
1903
1904 // 8.12: Кликаем на "Применить автофильтры вручную"
1905 showStatus('Применение автофильтров вручную...');
1906
1907 const applyButtonsAfterSave = document.querySelectorAll('button');
1908 let applyAutofilterButton = null;
1909
1910 for (const btn of applyButtonsAfterSave) {
1911 if (btn.textContent.trim() === 'Применить автофильтры вручную') {
1912 applyAutofilterButton = btn;
1913 console.log('Найдена кнопка "Применить автофильтры вручную"');
1914 break;
1915 }
1916 }
1917
1918 if (!applyAutofilterButton) {
1919 console.error('Кнопка "Применить автофильтры вручную" не найдена');
1920 showStatus('⚠️ Кнопка "Применить автофильтры вручную" не найдена', true);
1921 } else {
1922 applyAutofilterButton.click();
1923 console.log('Клик на "Применить автофильтры вручную" выполнен');
1924 await wait(1000);
1925 }
1926 }
1927 }
1928 }
1929 }
1930 }
1931 }
1932 }
1933 }
1934 }
1935 }
1936 }
1937 }
1938
1939 showStatus(`✅ Полностью готово! Стоимость корзины: ${cartCostRounded} ₽`);
1940 console.log('Стратегия и кластеры успешно настроены');
1941
1942 // Проверяем, есть ли массовая обработка
1943 const savedDRR = await GM.getValue('bulkProcessingDRR', null);
1944 if (savedDRR !== null) {
1945 console.log('Обнаружена массовая обработка, переходим к следующей кампании');
1946
1947 // Проверяем, не на паузе ли обработка
1948 const isPaused = await GM.getValue('bulkPaused', false);
1949 if (isPaused) {
1950 console.log('Обработка на паузе, ждем возобновления');
1951 showStatus('⏸️ Обработка на паузе');
1952 return;
1953 }
1954
1955 // Получаем список кампаний, текущий индекс и общее количество
1956 const campaignsJson = await GM.getValue('bulkCampaigns', null);
1957 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
1958 const totalCampaigns = await GM.getValue('bulkTotalCampaigns', 0);
1959
1960 if (campaignsJson) {
1961 const campaigns = JSON.parse(campaignsJson);
1962 const nextIndex = currentIndex + 1;
1963
1964 console.log(`Обработано кампаний: ${nextIndex} из ${totalCampaigns}`);
1965
1966 if (nextIndex < campaigns.length) {
1967 // Сохраняем новый индекс
1968 await GM.setValue('bulkCurrentIndex', nextIndex);
1969
1970 // Открываем следующую кампанию в новой вкладке через window.open
1971 console.log(`Открываем кампанию ${nextIndex + 1}/${totalCampaigns}...`);
1972 const newTab = window.open(campaigns[nextIndex], '_blank');
1973
1974 if (newTab) {
1975 // Закрываем текущую вкладку
1976 await wait(1000);
1977 window.close();
1978 } else {
1979 // Если блокировщик всплывающих окон, используем редирект
1980 console.log('Блокировщик всплывающих окон, используем редирект');
1981 window.location.href = campaigns[nextIndex];
1982 }
1983 } else {
1984 // Все кампании обработаны
1985 console.log('✅ Все кампании обработаны!');
1986 await GM.deleteValue('bulkProcessingDRR');
1987 await GM.deleteValue('bulkCampaigns');
1988 await GM.deleteValue('bulkCurrentIndex');
1989 await GM.deleteValue('bulkTotalCampaigns');
1990
1991 // Закрываем текущую вкладку
1992 showStatus('✅ Все кампании обработаны! Закрытие вкладки...');
1993 await wait(1000);
1994 window.close();
1995 }
1996 }
1997 }
1998
1999 } catch (error) {
2000 console.error('Ошибка при выполнении стратегии:', error);
2001 showStatus(`Ошибка: ${error.message}`, true);
2002 await handleStrategyError();
2003 }
2004 }
2005
2006 // Функция для обработки ошибок и перехода к следующей кампании
2007 async function handleStrategyError() {
2008 const savedDRR = await GM.getValue('bulkProcessingDRR', null);
2009 if (savedDRR !== null) {
2010 console.log('Обнаружена ошибка при массовой обработке, ждем 10 секунд перед переходом к следующей кампании');
2011 showStatus('⚠️ Ошибка! Переход к следующей кампании через 10 секунд...', true);
2012
2013 // Ждем 10 секунд
2014 for (let i = 10; i > 0; i--) {
2015 showStatus(`⚠️ Ошибка! Переход к следующей через ${i} сек...`, true);
2016 await wait(1000);
2017 }
2018
2019 // Получаем список кампаний и текущий индекс
2020 const campaignsJson = await GM.getValue('bulkCampaigns', null);
2021 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
2022
2023 if (campaignsJson) {
2024 const campaigns = JSON.parse(campaignsJson);
2025 const nextIndex = currentIndex + 1;
2026
2027 console.log(`Пропускаем кампанию с ошибкой. Переход к ${nextIndex + 1} из ${campaigns.length}`);
2028
2029 if (nextIndex < campaigns.length) {
2030 // Сохраняем новый индекс
2031 await GM.setValue('bulkCurrentIndex', nextIndex);
2032
2033 // Открываем следующую кампанию в новой вкладке через window.open
2034 console.log(`Открытие кампании ${nextIndex + 1}: ${campaigns[nextIndex]}`);
2035 const newTab = window.open(campaigns[nextIndex], '_blank');
2036
2037 if (newTab) {
2038 // Закрываем текущую вкладку
2039 await wait(1000);
2040 window.close();
2041 } else {
2042 // Если блокировщик всплывающих окон, используем редирект
2043 window.location.href = campaigns[nextIndex];
2044 }
2045 } else {
2046 // Все кампании обработаны
2047 console.log('Все кампании обработаны, очищаем данные');
2048 await GM.deleteValue('bulkProcessingDRR');
2049 await GM.deleteValue('bulkCampaigns');
2050 await GM.deleteValue('bulkCurrentIndex');
2051 await GM.deleteValue('bulkTotalCampaigns');
2052
2053 // Закрываем текущую вкладку
2054 showStatus('✅ Все кампании обработаны! Закрытие вкладки...');
2055 await wait(1000);
2056 window.close();
2057 }
2058 }
2059 }
2060 }
2061
2062 // Инициализация
2063 function init() {
2064 console.log('Инициализация расширения');
2065
2066 // Проверяем, на какой странице мы находимся
2067 if (window.location.href.includes('/advert/campaigns')) {
2068 // Страница со списком кампаний
2069 console.log('Страница списка кампаний обнаружена');
2070
2071 setTimeout(() => {
2072 if (document.body) {
2073 createBulkUI();
2074 } else {
2075 console.error('Body не найден');
2076 }
2077 }, 1000);
2078 } else if (window.location.href.includes('/campaigns/auto-campaigns/') && window.location.href.includes('/campaign')) {
2079 // Страница отдельной кампании
2080 console.log('Страница кампании обнаружена');
2081
2082 // Создаем UI с небольшой задержкой, чтобы убедиться, что страница загружена
2083 setTimeout(() => {
2084 if (document.body) {
2085 createUI();
2086 } else {
2087 console.error('Body не найден');
2088 }
2089 }, 1000);
2090 } else {
2091 console.log('Не на странице кампании, UI не создается');
2092 }
2093 }
2094
2095 // Запускаем инициализацию
2096 init();
2097
2098})();