Автоматическая установка стратегии рекламной кампании на основе статистики
Size
63.9 KB
Version
1.8.5
Created
Feb 9, 2026
Updated
about 1 month ago
1// ==UserScript==
2// @name MP Manager Auto Strategy Setter
3// @description Автоматическая установка стратегии рекламной кампании на основе статистики
4// @version 1.8.5
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 = 'eyJjYW1wYWlnblR5cGUiOiJTZWFyY2hDYXRhbG9nIiwidmVyc2lvbiI6MiwiZGF0YSI6eyJzdGF0ZSI6IkVuYWJsZWQiLCJtb2RlIjp7InR5cGUiOiJQb3NpdGlvbiIsInBhdXNlVHlwZSI6IlJlc3VtZSJ9LCJzdHJhdGVneSI6eyJ0eXBlIjoiQ2FtcGFpZ25TdGF0cyIsIm1vZGUiOiJQbGFjZSIsImJpZE1vZGUiOiJDb3N0UGVyQ2xpY2siLCJjYW1wYWlnblN0YXRzIjp7Im1heFByaWNlIjo2MCwibWluUHJpY2UiOjEsInBsYXRmb3JtcyI6W10sImludGVydmFsIjoiV2VlayIsInJ1bGVzIjpbeyJ0eXBlIjoiQ29zdFBlckFkZGVkVG9DYXJ0IiwibW9kZSI6IlZhbHVlIiwidmFsdWUiOiIifV19fSwicGxhY2VTdHJhdGVneSI6eyJ0eXBlIjoiS2V5d29yZHMiLCJrZXl3b3JkcyI6eyJrZXl3b3JkcyI6W119fSwic3RvY2tTdHJhdGVneSI6eyJtb2RlIjoiU3RvY2tzIn0sIm9yZGVyU3RyYXRlZ3kiOnsibW9kZSI6Ik9yZGVycyJ9LCJwcm9kdWN0UGxhY2VTdHJhdGVneSI6eyJwcm9kdWN0UGxhY2UiOiIifSwiaXNDbHVzdGVyIjpmYWxzZX19';
20
21 // Глобальные переменные для массовой обработки
22 let isBulkProcessing = false;
23 let bulkDesiredPercentage = 50;
24 let bulkPaused = false;
25
26 // Функция для получения сохраненных стратегий
27 async function getSavedStrategies() {
28 let strategies = await GM.getValue('saved_strategies', []);
29
30 // Миграция: добавляем ID к стратегиям, у которых его нет
31 let needsMigration = false;
32 strategies = strategies.map((strategy, index) => {
33 if (!strategy.id) {
34 needsMigration = true;
35 return {
36 ...strategy,
37 id: 'strategy_migrated_' + Date.now() + '_' + index
38 };
39 }
40 return strategy;
41 });
42
43 // Сохраняем обновленные стратегии, если была миграция
44 if (needsMigration) {
45 await GM.setValue('saved_strategies', strategies);
46 console.log('Миграция стратегий выполнена: добавлены ID');
47 }
48
49 // Если нет сохраненных стратегий, добавляем стратегию по умолчанию
50 if (strategies.length === 0) {
51 return [{
52 id: 'default',
53 name: 'Стратегия по умолчанию',
54 data: STRATEGY_CODE
55 }];
56 }
57 return strategies;
58 }
59
60 // Функция для получения текущей стратегии
61 async function getCurrentStrategy() {
62 const currentId = await GM.getValue('current_strategy_id', 'default');
63 const strategies = await getSavedStrategies();
64 const strategy = strategies.find(s => s.id === currentId);
65 return strategy ? strategy.data : STRATEGY_CODE;
66 }
67
68 // Функция для создания модального окна управления стратегиями
69 function createStrategyManagementModal() {
70 return new Promise(async (resolve) => {
71 // Создаем оверлей
72 const overlay = document.createElement('div');
73 overlay.style.cssText = `
74 position: fixed;
75 top: 0;
76 left: 0;
77 width: 100%;
78 height: 100%;
79 background: rgba(0, 0, 0, 0.5);
80 display: flex;
81 justify-content: center;
82 align-items: center;
83 z-index: 10001;
84 `;
85
86 // Создаем модальное окно
87 const modal = document.createElement('div');
88 modal.style.cssText = `
89 background: white;
90 border-radius: 12px;
91 padding: 24px;
92 min-width: 500px;
93 max-width: 600px;
94 max-height: 80vh;
95 overflow-y: auto;
96 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
97 `;
98
99 // Заголовок
100 const title = document.createElement('h2');
101 title.textContent = 'Управление стратегиями';
102 title.style.cssText = `
103 margin: 0 0 20px 0;
104 font-size: 24px;
105 font-weight: bold;
106 color: #333;
107 `;
108 modal.appendChild(title);
109
110 // Контейнер для списка стратегий
111 const listContainer = document.createElement('div');
112 listContainer.style.cssText = `
113 margin-bottom: 20px;
114 max-height: 300px;
115 overflow-y: auto;
116 `;
117
118 // Функция для обновления списка
119 const updateList = async () => {
120 const currentStrategies = await getSavedStrategies();
121 const selectedStrategy = await GM.getValue('current_strategy_id', 'default');
122 listContainer.innerHTML = '';
123
124 if (currentStrategies.length === 0) {
125 const emptyMessage = document.createElement('p');
126 emptyMessage.textContent = 'Нет сохраненных стратегий. Используется стратегия по умолчанию.';
127 emptyMessage.style.cssText = `
128 color: #666;
129 font-style: italic;
130 padding: 20px;
131 text-align: center;
132 `;
133 listContainer.appendChild(emptyMessage);
134 } else {
135 currentStrategies.forEach((strategy, index) => {
136 const item = document.createElement('div');
137 const isSelected = strategy.id === selectedStrategy;
138 item.style.cssText = `
139 display: flex;
140 justify-content: space-between;
141 align-items: center;
142 padding: 12px;
143 margin-bottom: 8px;
144 background: ${isSelected ? '#e3f2fd' : '#f5f5f5'};
145 border-radius: 8px;
146 border: ${isSelected ? '2px solid #2196f3' : '1px solid #ddd'};
147 `;
148
149 const nameSpan = document.createElement('span');
150 nameSpan.textContent = strategy.name + (isSelected ? ' ✓' : '');
151 nameSpan.style.cssText = `
152 font-size: 16px;
153 color: #333;
154 flex: 1;
155 font-weight: ${isSelected ? 'bold' : 'normal'};
156 `;
157
158 const buttonsContainer = document.createElement('div');
159 buttonsContainer.style.cssText = `
160 display: flex;
161 gap: 8px;
162 `;
163
164 // Кнопка выбора
165 if (!isSelected) {
166 const selectBtn = document.createElement('button');
167 selectBtn.textContent = '✓ Выбрать';
168 selectBtn.style.cssText = `
169 background: #2196f3;
170 color: white;
171 border: none;
172 padding: 6px 12px;
173 border-radius: 6px;
174 cursor: pointer;
175 font-size: 14px;
176 `;
177 selectBtn.onmouseover = () => selectBtn.style.background = '#1976d2';
178 selectBtn.onmouseout = () => selectBtn.style.background = '#2196f3';
179 selectBtn.onclick = async () => {
180 await GM.setValue('current_strategy_id', strategy.id);
181 console.log(`Выбрана стратегия: ${strategy.name} (ID: ${strategy.id})`);
182 await updateList();
183 };
184 buttonsContainer.appendChild(selectBtn);
185 }
186
187 // Кнопка удаления
188 const deleteBtn = document.createElement('button');
189 deleteBtn.textContent = '🗑️';
190 deleteBtn.style.cssText = `
191 background: #f44336;
192 color: white;
193 border: none;
194 padding: 6px 12px;
195 border-radius: 6px;
196 cursor: pointer;
197 font-size: 14px;
198 `;
199 deleteBtn.onmouseover = () => deleteBtn.style.background = '#d32f2f';
200 deleteBtn.onmouseout = () => deleteBtn.style.background = '#f44336';
201 deleteBtn.onclick = async () => {
202 if (confirm(`Удалить стратегию "${strategy.name}"?`)) {
203 currentStrategies.splice(index, 1);
204 await GM.setValue('saved_strategies', currentStrategies);
205 // Если удаляем выбранную стратегию, возвращаемся к дефолтной
206 if (isSelected) {
207 await GM.setValue('current_strategy_id', 'default');
208 }
209 await updateList();
210 }
211 };
212 buttonsContainer.appendChild(deleteBtn);
213
214 item.appendChild(nameSpan);
215 item.appendChild(buttonsContainer);
216 listContainer.appendChild(item);
217 });
218 }
219 };
220
221 await updateList();
222 modal.appendChild(listContainer);
223
224 // Кнопка добавления стратегии
225 const addButton = document.createElement('button');
226 addButton.textContent = '➕ Добавить новую стратегию';
227 addButton.style.cssText = `
228 width: 100%;
229 background: #4caf50;
230 color: white;
231 border: none;
232 padding: 12px;
233 border-radius: 8px;
234 cursor: pointer;
235 font-size: 16px;
236 font-weight: bold;
237 margin-bottom: 12px;
238 `;
239 addButton.onmouseover = () => addButton.style.background = '#45a049';
240 addButton.onmouseout = () => addButton.style.background = '#4caf50';
241 addButton.onclick = async () => {
242 const name = prompt('Введите название стратегии:');
243 if (!name) return;
244
245 const data = prompt('Вставьте код стратегии (скопируйте из MP Manager):');
246 if (!data) return;
247
248 const currentStrategies = await GM.getValue('saved_strategies', []);
249 const newId = 'strategy_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
250 currentStrategies.push({ id: newId, name, data });
251 await GM.setValue('saved_strategies', currentStrategies);
252 alert(`Стратегия "${name}" сохранена!`);
253 await updateList();
254 };
255 modal.appendChild(addButton);
256
257 // Кнопка закрытия
258 const closeButton = document.createElement('button');
259 closeButton.textContent = 'Закрыть';
260 closeButton.style.cssText = `
261 width: 100%;
262 background: #666;
263 color: white;
264 border: none;
265 padding: 12px;
266 border-radius: 8px;
267 cursor: pointer;
268 font-size: 16px;
269 `;
270 closeButton.onmouseover = () => closeButton.style.background = '#555';
271 closeButton.onmouseout = () => closeButton.style.background = '#666';
272 closeButton.onclick = () => {
273 document.body.removeChild(overlay);
274 resolve();
275 };
276 modal.appendChild(closeButton);
277
278 overlay.appendChild(modal);
279 document.body.appendChild(overlay);
280
281 // Закрытие по клику на оверлей
282 overlay.onclick = (e) => {
283 if (e.target === overlay) {
284 document.body.removeChild(overlay);
285 resolve();
286 }
287 };
288 });
289 }
290
291 // Функция для создания UI
292 function createUI() {
293 console.log('Создание UI панели');
294
295 // Проверяем, есть ли сохраненный ДРР для массовой обработки
296 GM.getValue('bulkProcessingDRR', null).then(savedDRR => {
297 if (savedDRR !== null) {
298 console.log(`Найден сохраненный ДРР для массовой обработки: ${savedDRR}`);
299 // Автоматически запускаем стратегию с сохраненным ДРР
300 setTimeout(() => {
301 const input = document.getElementById('desired-percentage');
302 if (input) {
303 input.value = savedDRR;
304 // Запускаем стратегию автоматически
305 runStrategy();
306 }
307 }, 2000);
308 }
309 });
310
311 const panel = document.createElement('div');
312 panel.id = 'auto-strategy-panel';
313 panel.style.cssText = `
314 position: fixed;
315 top: 20px;
316 right: 20px;
317 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
318 padding: 15px;
319 border-radius: 12px;
320 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
321 z-index: 10000;
322 min-width: 280px;
323 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
324 color: white;
325 transition: all 0.3s ease;
326 cursor: move;
327 `;
328
329 panel.innerHTML = `
330 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; cursor: pointer;" id="panel-header">
331 <h3 style="margin: 0; font-size: 16px; font-weight: 600;">🚀 Авто-стратегия</h3>
332 <span id="toggle-icon" style="font-size: 18px;">▼</span>
333 </div>
334
335 <div id="panel-content" style="display: none;">
336 <div style="margin-bottom: 15px;">
337 <label style="display: block; margin-bottom: 8px; font-size: 13px; font-weight: 500;">
338 Желаемый % рекламных расходов:
339 </label>
340 <input
341 type="number"
342 id="desired-percentage"
343 value="50"
344 min="1"
345 max="100"
346 step="0.1"
347 style="
348 width: 100%;
349 padding: 10px;
350 border: none;
351 border-radius: 8px;
352 font-size: 14px;
353 box-sizing: border-box;
354 background: rgba(255, 255, 255, 0.95);
355 color: #333;
356 cursor: text;
357 "
358 />
359 </div>
360
361 <button
362 id="run-strategy-btn"
363 style="
364 width: 100%;
365 padding: 12px;
366 background: white;
367 color: #667eea;
368 border: none;
369 border-radius: 8px;
370 font-size: 14px;
371 font-weight: 600;
372 cursor: pointer;
373 transition: all 0.3s ease;
374 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
375 margin-bottom: 10px;
376 "
377 onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 16px rgba(0, 0, 0, 0.2)';"
378 onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 12px rgba(0, 0, 0, 0.15)';"
379 >
380 Запустить
381 </button>
382
383 <button
384 id="manage-strategies-btn"
385 style="
386 width: 100%;
387 padding: 10px;
388 background: rgba(255, 255, 255, 0.2);
389 color: white;
390 border: 1px solid rgba(255, 255, 255, 0.3);
391 border-radius: 8px;
392 font-size: 13px;
393 font-weight: 600;
394 cursor: pointer;
395 transition: all 0.3s ease;
396 "
397 onmouseover="this.style.background='rgba(255, 255, 255, 0.3)';"
398 onmouseout="this.style.background='rgba(255, 255, 255, 0.2)';"
399 >
400 ⚙️ Управление стратегиями
401 </button>
402
403 <div id="status-message" style="
404 margin-top: 15px;
405 padding: 10px;
406 border-radius: 8px;
407 font-size: 12px;
408 background: rgba(255, 255, 255, 0.2);
409 display: none;
410 "></div>
411 </div>
412 `;
413
414 document.body.appendChild(panel);
415 console.log('UI панель создана');
416
417 // Добавляем возможность перетаскивания
418 makeDraggable(panel);
419
420 // Добавляем обработчик клика на заголовок для разворачивания/сворачивания
421 document.getElementById('panel-header').addEventListener('click', () => {
422 const content = document.getElementById('panel-content');
423 const icon = document.getElementById('toggle-icon');
424 if (content.style.display === 'none') {
425 content.style.display = 'block';
426 icon.textContent = '▲';
427 } else {
428 content.style.display = 'none';
429 icon.textContent = '▼';
430 }
431 });
432
433 // Добавляем обработчик клика на кнопку
434 document.getElementById('run-strategy-btn').addEventListener('click', runStrategy);
435
436 // Добавляем обработчик для кнопки управления стратегиями
437 document.getElementById('manage-strategies-btn').addEventListener('click', async () => {
438 await createStrategyManagementModal();
439 });
440 }
441
442 // Функция для создания UI панели массового изменения
443 function createBulkUI() {
444 console.log('Создание UI панели массового изменения');
445
446 const panel = document.createElement('div');
447 panel.id = 'bulk-strategy-panel';
448 panel.style.cssText = `
449 position: fixed;
450 top: 20px;
451 right: 20px;
452 background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
453 padding: 15px;
454 border-radius: 12px;
455 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
456 z-index: 10000;
457 min-width: 300px;
458 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
459 color: white;
460 transition: all 0.3s ease;
461 cursor: move;
462 `;
463
464 panel.innerHTML = `
465 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; cursor: pointer;" id="bulk-panel-header">
466 <h3 style="margin: 0; font-size: 16px; font-weight: 600;">📦 Массовое изменение</h3>
467 <span id="bulk-toggle-icon" style="font-size: 18px;">▼</span>
468 </div>
469
470 <div id="bulk-panel-content" style="display: none;">
471 <div style="margin-bottom: 15px;">
472 <label style="display: block; margin-bottom: 8px; font-size: 13px; font-weight: 500;">
473 Желаемый % рекламных расходов (ДРР):
474 </label>
475 <input
476 type="number"
477 id="bulk-desired-percentage"
478 value="50"
479 min="1"
480 max="100"
481 step="0.1"
482 style="
483 width: 100%;
484 padding: 10px;
485 border: none;
486 border-radius: 8px;
487 font-size: 14px;
488 box-sizing: border-box;
489 background: rgba(255, 255, 255, 0.95);
490 color: #333;
491 cursor: text;
492 "
493 />
494 </div>
495
496 <div id="bulk-campaigns-info" style="
497 margin-bottom: 15px;
498 padding: 10px;
499 border-radius: 8px;
500 font-size: 12px;
501 background: rgba(255, 255, 255, 0.2);
502 ">
503 Найдено кампаний: <span id="campaigns-count">0</span>
504 </div>
505
506 <button
507 id="run-bulk-strategy-btn"
508 style="
509 width: 100%;
510 padding: 12px;
511 background: white;
512 color: #f5576c;
513 border: none;
514 border-radius: 8px;
515 font-size: 14px;
516 font-weight: 600;
517 cursor: pointer;
518 transition: all 0.3s ease;
519 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
520 margin-bottom: 10px;
521 "
522 onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 16px rgba(0, 0, 0, 0.2)';"
523 onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 12px rgba(0, 0, 0, 0.15)';"
524 >
525 Запустить
526 </button>
527
528 <button
529 id="bulk-manage-strategies-btn"
530 style="
531 width: 100%;
532 padding: 10px;
533 background: rgba(255, 255, 255, 0.2);
534 color: white;
535 border: 1px solid rgba(255, 255, 255, 0.3);
536 border-radius: 8px;
537 font-size: 13px;
538 font-weight: 600;
539 cursor: pointer;
540 transition: all 0.3s ease;
541 margin-bottom: 10px;
542 "
543 onmouseover="this.style.background='rgba(255, 255, 255, 0.3)';"
544 onmouseout="this.style.background='rgba(255, 255, 255, 0.2)';"
545 >
546 ⚙️ Управление стратегиями
547 </button>
548
549 <div id="bulk-control-buttons" style="
550 margin-top: 10px;
551 display: none;
552 gap: 10px;
553 ">
554 <button
555 id="pause-bulk-btn"
556 style="
557 flex: 1;
558 padding: 10px;
559 background: rgba(255, 255, 255, 0.9);
560 color: #f5576c;
561 border: none;
562 border-radius: 8px;
563 font-size: 13px;
564 font-weight: 600;
565 cursor: pointer;
566 transition: all 0.3s ease;
567 "
568 >
569 ⏸️ Пауза
570 </button>
571 <button
572 id="stop-bulk-btn"
573 style="
574 flex: 1;
575 padding: 10px;
576 background: rgba(255, 59, 48, 0.9);
577 color: white;
578 border: none;
579 border-radius: 8px;
580 font-size: 13px;
581 font-weight: 600;
582 cursor: pointer;
583 transition: all 0.3s ease;
584 "
585 >
586 ⏹️ Стоп
587 </button>
588 </div>
589
590 <div id="bulk-status-message" style="
591 margin-top: 15px;
592 padding: 10px;
593 border-radius: 8px;
594 font-size: 12px;
595 background: rgba(255, 255, 255, 0.2);
596 display: none;
597 "></div>
598
599 <div id="bulk-progress" style="
600 margin-top: 15px;
601 display: none;
602 ">
603 <div style="margin-bottom: 5px; font-size: 12px;">
604 Прогресс: <span id="bulk-progress-text">0/0</span>
605 </div>
606 <div style="
607 width: 100%;
608 height: 8px;
609 background: rgba(255, 255, 255, 0.3);
610 border-radius: 4px;
611 overflow: hidden;
612 ">
613 <div id="bulk-progress-bar" style="
614 width: 0%;
615 height: 100%;
616 background: white;
617 transition: width 0.3s ease;
618 "></div>
619 </div>
620 </div>
621 </div>
622 `;
623
624 document.body.appendChild(panel);
625 console.log('UI панель массового изменения создана');
626
627 // Добавляем возможность перетаскивания
628 makeDraggable(panel);
629
630 // Подсчитываем количество кампаний
631 updateCampaignsCount();
632
633 // Добавляем обработчик клика на заголовок для разворачивания/сворачивания
634 document.getElementById('bulk-panel-header').addEventListener('click', () => {
635 const content = document.getElementById('bulk-panel-content');
636 const icon = document.getElementById('bulk-toggle-icon');
637 if (content.style.display === 'none') {
638 content.style.display = 'block';
639 icon.textContent = '▲';
640 } else {
641 content.style.display = 'none';
642 icon.textContent = '▼';
643 }
644 });
645
646 // Добавляем обработчик клика на кнопку
647 document.getElementById('run-bulk-strategy-btn').addEventListener('click', runBulkStrategy);
648
649 // Добавляем обработчик для кнопки управления стратегиями
650 document.getElementById('bulk-manage-strategies-btn').addEventListener('click', async () => {
651 await createStrategyManagementModal();
652 });
653
654 // Добавляем обработчики для кнопок управления
655 document.getElementById('pause-bulk-btn').addEventListener('click', pauseBulkProcessing);
656 document.getElementById('stop-bulk-btn').addEventListener('click', stopBulkProcessing);
657 }
658
659 // Функция для создания перетаскиваемого элемента
660 function makeDraggable(element) {
661 let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
662 let isDragging = false;
663
664 element.onmousedown = dragMouseDown;
665
666 function dragMouseDown(e) {
667 // Не перетаскиваем, если кликнули на input, button или другие интерактивные элементы
668 if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.tagName === 'TEXTAREA') {
669 return;
670 }
671
672 e.preventDefault();
673 isDragging = true;
674 pos3 = e.clientX;
675 pos4 = e.clientY;
676 document.onmouseup = closeDragElement;
677 document.onmousemove = elementDrag;
678 }
679
680 function elementDrag(e) {
681 if (!isDragging) return;
682 e.preventDefault();
683 pos1 = pos3 - e.clientX;
684 pos2 = pos4 - e.clientY;
685 pos3 = e.clientX;
686 pos4 = e.clientY;
687 element.style.top = (element.offsetTop - pos2) + 'px';
688 element.style.left = (element.offsetLeft - pos1) + 'px';
689 element.style.right = 'auto';
690 element.style.bottom = 'auto';
691 }
692
693 function closeDragElement() {
694 isDragging = false;
695 document.onmouseup = null;
696 document.onmousemove = null;
697 }
698 }
699
700 // Функция для обновления количества кампаний
701 function updateCampaignsCount() {
702 const campaignLinks = document.querySelectorAll('a[href*="/campaigns/auto-campaigns/"][href*="/campaign"]');
703 const count = campaignLinks.length;
704 const countElement = document.getElementById('campaigns-count');
705 if (countElement) {
706 countElement.textContent = count;
707 }
708 console.log(`Найдено кампаний: ${count}`);
709 }
710
711 // Функция для показа статуса массовой обработки
712 function showBulkStatus(message, isError = false) {
713 const statusDiv = document.getElementById('bulk-status-message');
714 if (statusDiv) {
715 statusDiv.textContent = message;
716 statusDiv.style.display = 'block';
717 statusDiv.style.background = isError ? 'rgba(255, 59, 48, 0.9)' : 'rgba(52, 199, 89, 0.9)';
718 console.log(`Статус массовой обработки: ${message}`);
719 }
720 }
721
722 // Функция для обновления прогресса
723 function updateBulkProgress(current, total) {
724 const progressDiv = document.getElementById('bulk-progress');
725 const progressText = document.getElementById('bulk-progress-text');
726 const progressBar = document.getElementById('bulk-progress-bar');
727
728 if (progressDiv && progressText && progressBar) {
729 progressDiv.style.display = 'block';
730 progressText.textContent = `${current}/${total}`;
731 const percentage = (current / total) * 100;
732 progressBar.style.width = `${percentage}%`;
733 }
734 }
735
736 // Функция для получения всех ссылок на кампании
737 function getAllCampaignLinks() {
738 const campaignLinks = Array.from(document.querySelectorAll('a[href*="/campaigns/auto-campaigns/"][href*="/campaign"]'));
739 return campaignLinks.map(link => ({
740 url: link.href,
741 name: link.textContent.trim()
742 }));
743 }
744
745 // Функция для прокрутки страницы вниз для загрузки всех кампаний
746 async function scrollToLoadAllCampaigns() {
747 console.log('Начало прокрутки для загрузки всех кампаний');
748 showBulkStatus('Загрузка всех кампаний...');
749
750 // Находим контейнер с прокруткой (ID динамический, поэтому используем атрибутный селектор)
751 const scrollContainer = document.querySelector('[id^="table-"][id$="-container"]');
752 if (!scrollContainer) {
753 console.error('Контейнер для прокрутки не найден');
754 return [];
755 }
756
757 console.log(`Контейнер найден: ${scrollContainer.id}. ScrollHeight: ${scrollContainer.scrollHeight}, ClientHeight: ${scrollContainer.clientHeight}`);
758
759 // Используем Set для хранения уникальных ссылок на кампании
760 const allCampaignLinks = new Set();
761
762 let previousCount = 0;
763 let currentCount = 0;
764 let stableCount = 0;
765 let attempts = 0;
766 const maxAttempts = 100;
767 const scrollStep = 300; // Прокручиваем по 300px за раз для виртуализированной таблицы
768
769 while (attempts < maxAttempts) {
770 // Прокручиваем контейнер постепенно вниз
771 const currentScrollTop = scrollContainer.scrollTop;
772 const maxScrollTop = scrollContainer.scrollHeight - scrollContainer.clientHeight;
773
774 // Прокручиваем на один шаг вниз
775 scrollContainer.scrollTop = Math.min(currentScrollTop + scrollStep, maxScrollTop);
776
777 console.log(`Прокрутка: ${scrollContainer.scrollTop} / ${maxScrollTop}`);
778
779 // Ждем, чтобы виртуализированная таблица успела отрендерить новые элементы
780 await wait(1500);
781
782 // Собираем все видимые ссылки на кампании
783 const visibleLinks = document.querySelectorAll('a[href*="/campaigns/auto-campaigns/"][href*="/campaign"]');
784 visibleLinks.forEach(link => {
785 allCampaignLinks.add(JSON.stringify({
786 url: link.href,
787 name: link.textContent.trim()
788 }));
789 });
790
791 currentCount = allCampaignLinks.size;
792 console.log(`Попытка ${attempts + 1}: собрано ${currentCount} уникальных кампаний`);
793
794 // Обновляем статус
795 showBulkStatus(`Загружено ${currentCount} кампаний...`);
796
797 // Если количество не изменилось
798 if (currentCount === previousCount) {
799 stableCount++;
800 console.log(`Количество стабильно: ${stableCount} раз подряд`);
801
802 // Если достигли конца и количество стабильно 3 раза подряд
803 if (stableCount >= 3 && scrollContainer.scrollTop >= maxScrollTop - 10) {
804 console.log('Все кампании загружены');
805 break;
806 }
807 } else {
808 // Количество изменилось, сбрасываем счетчик
809 stableCount = 0;
810 }
811
812 previousCount = currentCount;
813 attempts++;
814
815 // Если достигли конца контейнера
816 if (scrollContainer.scrollTop >= maxScrollTop - 10) {
817 console.log('Достигнут конец контейнера, ждем финальной загрузки...');
818 await wait(2000);
819
820 // Собираем последние видимые ссылки
821 const finalLinks = document.querySelectorAll('a[href*="/campaigns/auto-campaigns/"][href*="/campaign"]');
822 finalLinks.forEach(link => {
823 allCampaignLinks.add(JSON.stringify({
824 url: link.href,
825 name: link.textContent.trim()
826 }));
827 });
828
829 const finalCount = allCampaignLinks.size;
830 if (finalCount === currentCount) {
831 console.log('Подтверждено: все кампании загружены');
832 currentCount = finalCount;
833 break;
834 }
835 currentCount = finalCount;
836 }
837 }
838
839 // Прокручиваем обратно наверх
840 scrollContainer.scrollTop = 0;
841 await wait(500);
842
843 console.log(`Загружено кампаний: ${currentCount}`);
844 showBulkStatus(`Загружено ${currentCount} кампаний`);
845
846 // Преобразуем Set обратно в массив объектов
847 const campaignsArray = Array.from(allCampaignLinks).map(item => JSON.parse(item));
848
849 return campaignsArray;
850 }
851
852 // Функция для массовой обработки кампаний
853 async function runBulkStrategy() {
854 if (isBulkProcessing) {
855 showBulkStatus('Обработка уже выполняется', true);
856 return;
857 }
858
859 try {
860 isBulkProcessing = true;
861 console.log('Начало массовой обработки кампаний');
862
863 bulkDesiredPercentage = parseFloat(document.getElementById('bulk-desired-percentage').value);
864 if (!bulkDesiredPercentage || bulkDesiredPercentage <= 0) {
865 showBulkStatus('Ошибка: введите корректный процент', true);
866 isBulkProcessing = false;
867 return;
868 }
869
870 // Сохраняем ДРР для автоматической обработки
871 await GM.setValue('bulkProcessingDRR', bulkDesiredPercentage);
872 console.log(`ДРР ${bulkDesiredPercentage} сохранен для массовой обработки`);
873
874 // Прокручиваем страницу для загрузки всех кампаний
875 const campaigns = await scrollToLoadAllCampaigns();
876
877 if (campaigns.length === 0) {
878 showBulkStatus('Ошибка: кампании не найдены', true);
879 isBulkProcessing = false;
880 await GM.deleteValue('bulkProcessingDRR');
881 return;
882 }
883
884 console.log(`Найдено кампаний для обработки: ${campaigns.length}`);
885 showBulkStatus(`Начинаем обработку ${campaigns.length} кампаний...`);
886 updateBulkProgress(0, campaigns.length);
887
888 // Сохраняем список кампаний и текущий индекс
889 await GM.setValue('bulkCampaigns', JSON.stringify(campaigns));
890 await GM.setValue('bulkCurrentIndex', 0);
891
892 // Показываем кнопки управления
893 const controlButtons = document.getElementById('bulk-control-buttons');
894 if (controlButtons) {
895 controlButtons.style.display = 'flex';
896 }
897
898 // Открываем первую кампанию в новой вкладке
899 if (campaigns.length > 0) {
900 console.log(`Открытие первой кампании: ${campaigns[0].name}`);
901 await GM.openInTab(campaigns[0].url, false);
902 }
903
904 } catch (error) {
905 console.error('Ошибка при массовой обработке:', error);
906 showBulkStatus(`Ошибка: ${error.message}`, true);
907 isBulkProcessing = false;
908 await GM.deleteValue('bulkProcessingDRR');
909 }
910 }
911
912 // Функция для паузы массовой обработки
913 async function pauseBulkProcessing() {
914 bulkPaused = !bulkPaused;
915 const pauseBtn = document.getElementById('pause-bulk-btn');
916
917 if (bulkPaused) {
918 await GM.setValue('bulkPaused', true);
919 pauseBtn.textContent = '▶️ Продолжить';
920 showBulkStatus('⏸️ Обработка приостановлена');
921 console.log('Массовая обработка приостановлена');
922 } else {
923 await GM.deleteValue('bulkPaused');
924 pauseBtn.textContent = '⏸️ Пауза';
925 showBulkStatus('▶️ Обработка возобновлена');
926 console.log('Массовая обработка возобновлена');
927
928 // Продолжаем обработку - открываем следующую кампанию
929 const campaignsJson = await GM.getValue('bulkCampaigns', null);
930 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
931
932 if (campaignsJson) {
933 const campaigns = JSON.parse(campaignsJson);
934 if (currentIndex < campaigns.length) {
935 console.log(`Продолжение с кампании ${currentIndex + 1}: ${campaigns[currentIndex].name}`);
936 await GM.openInTab(campaigns[currentIndex].url, false);
937 }
938 }
939 }
940 }
941
942 // Функция для остановки массовой обработки
943 async function stopBulkProcessing() {
944 await GM.deleteValue('bulkProcessingDRR');
945 await GM.deleteValue('bulkCampaigns');
946 await GM.deleteValue('bulkCurrentIndex');
947 await GM.deleteValue('bulkPaused');
948
949 showBulkStatus('⏹️ Обработка остановлена', true);
950 console.log('Массовая обработка остановлена');
951
952 // Скрываем кнопки управления
953 const controlButtons = document.getElementById('bulk-control-buttons');
954 if (controlButtons) {
955 controlButtons.style.display = 'none';
956 }
957
958 isBulkProcessing = false;
959 bulkPaused = false;
960 }
961
962 // Функция для показа статуса
963 function showStatus(message, isError = false) {
964 const statusDiv = document.getElementById('status-message');
965 if (statusDiv) {
966 statusDiv.textContent = message;
967 statusDiv.style.display = 'block';
968 statusDiv.style.background = isError ? 'rgba(255, 59, 48, 0.9)' : 'rgba(52, 199, 89, 0.9)';
969 console.log(`Статус: ${message}`);
970 }
971 }
972
973 // Функция для ожидания
974 function wait(ms) {
975 return new Promise(resolve => setTimeout(resolve, ms));
976 }
977
978 // Функция для парсинга числа из строки
979 function parseNumber(str) {
980 if (!str) return 0;
981 // Убираем все символы кроме цифр, точек и запятых
982 const cleaned = str.replace(/[^\d.,]/g, '').replace(/\s/g, '');
983 // Заменяем запятую на точку
984 const normalized = cleaned.replace(',', '.');
985 return parseFloat(normalized) || 0;
986 }
987
988 // Функция для парсинга процента
989 function parsePercentage(str) {
990 if (!str) return 0;
991 const cleaned = str.replace('%', '').replace(',', '.').trim();
992 return parseFloat(cleaned) || 0;
993 }
994
995 // Основная функция запуска стратегии
996 async function runStrategy() {
997 try {
998 console.log('Начало выполнения стратегии');
999 showStatus('Запуск процесса...');
1000
1001 const desiredPercentage = parseFloat(document.getElementById('desired-percentage').value);
1002 if (!desiredPercentage || desiredPercentage <= 0) {
1003 showStatus('Ошибка: введите корректный процент', true);
1004 await handleStrategyError();
1005 return;
1006 }
1007 console.log(`Желаемый процент: ${desiredPercentage}%`);
1008
1009 // Шаг 1: Кликаем на статистику
1010 showStatus('Открытие статистики...');
1011 const statsButton = document.querySelector('.css-amj7dw');
1012 if (!statsButton) {
1013 showStatus('Ошибка: кнопка статистики не найдена', true);
1014 await handleStrategyError();
1015 return;
1016 }
1017 statsButton.click();
1018 console.log('Клик на статистику выполнен');
1019 await wait(2000); // Увеличена задержка для загрузки статистики
1020
1021 // Шаг 1.5: Выбираем режим "Товары"
1022 showStatus('Выбор режима "Товары"...');
1023 const modeLabels = Array.from(document.querySelectorAll('label'));
1024 let modeInput = null;
1025
1026 for (const label of modeLabels) {
1027 if (label.textContent.trim() === 'Режим') {
1028 const inputId = label.getAttribute('for');
1029 if (inputId) {
1030 modeInput = document.getElementById(inputId);
1031 console.log(`Найдено поле Режим с id: ${inputId}`);
1032 break;
1033 }
1034 }
1035 }
1036
1037 if (modeInput) {
1038 // Устанавливаем значение "Товары" напрямую
1039 modeInput.value = 'Товары';
1040
1041 // Триггерим события для React
1042 const inputEvent = new Event('input', { bubbles: true });
1043 const changeEvent = new Event('change', { bubbles: true });
1044 modeInput.dispatchEvent(inputEvent);
1045 modeInput.dispatchEvent(changeEvent);
1046
1047 console.log('Установлено значение: Товары');
1048 await wait(1000); // Увеличена задержка для загрузки опций
1049
1050 // Проверяем, появились ли опции в выпадающем списке
1051 const options = document.querySelectorAll('[role="option"]');
1052 console.log(`Найдено опций: ${options.length}`);
1053
1054 if (options.length > 0) {
1055 // Ищем опцию "Товары" и кликаем на неё
1056 const tovarOption = Array.from(options).find(opt => opt.textContent.includes('Товары'));
1057 if (tovarOption) {
1058 tovarOption.click();
1059 console.log('Выбран режим "Товары"');
1060 await wait(1500); // Увеличена задержка для загрузки данных после выбора режима
1061 }
1062 }
1063 }
1064
1065 // Шаг 2: Извлекаем данные из статистики
1066 showStatus('Извлечение данных...');
1067
1068 // Ждем дополнительное время для загрузки статистики
1069 await wait(1000);
1070
1071 const stats = Array.from(document.querySelectorAll('.MuiTypography-caption.css-1et52kr'));
1072
1073 let sumOrders = 0;
1074 let ordersCount = 0;
1075 let cartToOrderPercent = 0;
1076
1077 stats.forEach(el => {
1078 const text = el.textContent.trim();
1079 const valueElement = el.closest('.MuiBox-root')?.querySelector('.MuiTypography-h3 .MuiTypography-body1');
1080 const value = valueElement ? valueElement.textContent.trim() : '';
1081
1082 console.log(`Найден показатель: ${text} = ${value}`);
1083
1084 if (text === 'Сумма заказов') {
1085 sumOrders = parseNumber(value);
1086 console.log(`Сумма заказов: ${sumOrders}`);
1087 } else if (text === 'Заказов') {
1088 ordersCount = parseNumber(value);
1089 console.log(`Заказов: ${ordersCount}`);
1090 } else if (text === 'Корзина → Заказ') {
1091 cartToOrderPercent = parsePercentage(value);
1092 console.log(`Корзина → Заказ: ${cartToOrderPercent}%`);
1093 }
1094 });
1095
1096 if (sumOrders === 0 || ordersCount === 0 || cartToOrderPercent === 0) {
1097 showStatus('Ошибка: не удалось получить данные статистики', true);
1098 console.error('Недостаточно данных:', { sumOrders, ordersCount, cartToOrderPercent });
1099 await handleStrategyError();
1100 return;
1101 }
1102
1103 // Шаг 3: Вычисляем стоимость корзины
1104 const cartCost = (sumOrders / ordersCount) * (desiredPercentage / 100) * (cartToOrderPercent / 100);
1105 const cartCostRounded = Math.round(cartCost * 100) / 100;
1106 console.log(`Расчет: (${sumOrders} / ${ordersCount}) * (${desiredPercentage} / 100) * (${cartToOrderPercent} / 100) = ${cartCostRounded}`);
1107 showStatus(`Рассчитано: ${cartCostRounded} ₽`);
1108
1109 // Закрываем статистику
1110 statsButton.click();
1111 await wait(500);
1112
1113 // Шаг 4: Кликаем на "Вставить стратегию"
1114 showStatus('Вставка стратегии...');
1115 const insertButtons = document.querySelectorAll('.css-5kbhos');
1116 let insertButton = null;
1117
1118 // Ищем кнопку с текстом "Вставить стратегию"
1119 for (const btn of insertButtons) {
1120 if (btn.textContent.includes('Вставить стратегию')) {
1121 insertButton = btn;
1122 break;
1123 }
1124 }
1125
1126 if (!insertButton && insertButtons.length >= 2) {
1127 insertButton = insertButtons[1];
1128 } else if (!insertButton && insertButtons.length === 1) {
1129 insertButton = insertButtons[0];
1130 }
1131
1132 if (!insertButton) {
1133 showStatus('Ошибка: кнопка "Вставить стратегию" не найдена', true);
1134 await handleStrategyError();
1135 return;
1136 }
1137
1138 // Получаем текущую выбранную стратегию
1139 const currentStrategyCode = await getCurrentStrategy();
1140 console.log('Используем стратегию, длина:', currentStrategyCode.length);
1141
1142 // Копируем код стратегии в буфер обмена ПЕРЕД открытием модального окна
1143 console.log('Копируем стратегию в буфер обмена...');
1144 try {
1145 // Способ 1: navigator.clipboard (современный)
1146 await navigator.clipboard.writeText(currentStrategyCode);
1147 console.log('✓ Стратегия скопирована через navigator.clipboard');
1148 } catch (clipboardError) {
1149 console.log('navigator.clipboard не сработал, пробуем GM.setClipboard...');
1150 // Способ 2: GM.setClipboard (fallback)
1151 await GM.setClipboard(currentStrategyCode);
1152 console.log('✓ Стратегия скопирована через GM.setClipboard');
1153 }
1154
1155 console.log('Длина стратегии:', currentStrategyCode.length);
1156 await wait(500);
1157
1158 insertButton.click();
1159 console.log('Клик на "Вставить стратегию" выполнен');
1160 await wait(1500); // Увеличена задержка для применения стратегии из буфера
1161
1162 console.log('✓ Стратегия вставлена из буфера обмена');
1163
1164 await wait(1000);
1165
1166 // Шаг 5: Находим поле "Желаемое значение" и вставляем результат
1167 showStatus('Заполнение поля...');
1168
1169 // Ищем поле с текстом "За корзину" или "Желаемое значение"
1170 const inputLabels = Array.from(document.querySelectorAll('.MuiInputLabel-root'));
1171 let targetInput = null;
1172
1173 for (const label of inputLabels) {
1174 const labelText = label.textContent;
1175 if (labelText.includes('За корзину') || labelText.includes('Желаемое значение')) {
1176 const inputId = label.getAttribute('for');
1177 if (inputId) {
1178 targetInput = document.getElementById(inputId);
1179 console.log(`Найдено поле: ${labelText}`);
1180 break;
1181 }
1182 }
1183 }
1184
1185 // Если не нашли по лейблу, ищем по имени
1186 if (!targetInput) {
1187 const inputs = document.querySelectorAll('input[type="number"]');
1188 for (const input of inputs) {
1189 const name = input.getAttribute('name') || '';
1190 if (name.includes('CostPerAddedToCart') || name.includes('value')) {
1191 targetInput = input;
1192 console.log(`Найдено поле по имени: ${name}`);
1193 break;
1194 }
1195 }
1196 }
1197
1198 if (!targetInput) {
1199 showStatus('Ошибка: поле для ввода не найдено', true);
1200 console.error('Не удалось найти поле для ввода значения');
1201 await handleStrategyError();
1202 return;
1203 }
1204
1205 // Устанавливаем значение
1206 targetInput.focus();
1207 targetInput.value = cartCostRounded.toString();
1208
1209 // Триггерим события для React
1210 const inputEvent = new Event('input', { bubbles: true });
1211 const changeEvent = new Event('change', { bubbles: true });
1212 targetInput.dispatchEvent(inputEvent);
1213 targetInput.dispatchEvent(changeEvent);
1214
1215 console.log(`Значение ${cartCostRounded} установлено в поле`);
1216 await wait(500);
1217
1218 // Шаг 7: Нажимаем "Сохранить"
1219 showStatus('Сохранение...');
1220 const saveButtons = document.querySelectorAll('button');
1221 let saveButton = null;
1222
1223 for (const btn of saveButtons) {
1224 if (btn.textContent.trim() === 'Сохранить') {
1225 saveButton = btn;
1226 break;
1227 }
1228 }
1229
1230 if (!saveButton) {
1231 showStatus('Ошибка: кнопка "Сохранить" не найдена', true);
1232 await handleStrategyError();
1233 return;
1234 }
1235
1236 saveButton.click();
1237 console.log('Клик на "Сохранить" выполнен');
1238 await wait(1000);
1239
1240 showStatus(`✅ Готово! Стоимость корзины: ${cartCostRounded} ₽`);
1241 console.log('Стратегия успешно установлена');
1242
1243 // Проверяем, есть ли массовая обработка
1244 const savedDRR = await GM.getValue('bulkProcessingDRR', null);
1245 if (savedDRR !== null) {
1246 console.log('Обнаружена массовая обработка, переходим к следующей кампании');
1247
1248 // Получаем список кампаний и текущий индекс
1249 const campaignsJson = await GM.getValue('bulkCampaigns', null);
1250 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
1251
1252 if (campaignsJson) {
1253 const campaigns = JSON.parse(campaignsJson);
1254 const nextIndex = currentIndex + 1;
1255
1256 console.log(`Обработано ${nextIndex} из ${campaigns.length} кампаний`);
1257
1258 if (nextIndex < campaigns.length) {
1259 // Сохраняем новый индекс
1260 await GM.setValue('bulkCurrentIndex', nextIndex);
1261
1262 // Открываем следующую кампанию в новой вкладке
1263 console.log(`Открытие кампании ${nextIndex + 1}: ${campaigns[nextIndex].name}`);
1264 await GM.openInTab(campaigns[nextIndex].url, false);
1265
1266 // Закрываем текущую вкладку
1267 await wait(1000);
1268 window.close();
1269 } else {
1270 // Все кампании обработаны
1271 console.log('Все кампании обработаны, очищаем данные');
1272 await GM.deleteValue('bulkProcessingDRR');
1273 await GM.deleteValue('bulkCampaigns');
1274 await GM.deleteValue('bulkCurrentIndex');
1275
1276 // Закрываем текущую вкладку
1277 showStatus('✅ Все кампании обработаны! Закрытие вкладки...');
1278 await wait(2000);
1279 window.close();
1280 }
1281 }
1282 }
1283
1284 } catch (error) {
1285 console.error('Ошибка при выполнении стратегии:', error);
1286 showStatus(`Ошибка: ${error.message}`, true);
1287 await handleStrategyError();
1288 }
1289 }
1290
1291 // Функция для обработки ошибок и перехода к следующей кампании
1292 async function handleStrategyError() {
1293 const savedDRR = await GM.getValue('bulkProcessingDRR', null);
1294 if (savedDRR !== null) {
1295 console.log('Обнаружена ошибка при массовой обработке, ждем 10 секунд перед переходом к следующей кампании');
1296 showStatus('⚠️ Ошибка! Переход к следующей кампании через 10 секунд...', true);
1297
1298 // Ждем 10 секунд
1299 for (let i = 10; i > 0; i--) {
1300 showStatus(`⚠️ Ошибка! Переход к следующей через ${i} сек...`, true);
1301 await wait(1000);
1302 }
1303
1304 // Получаем список кампаний и текущий индекс
1305 const campaignsJson = await GM.getValue('bulkCampaigns', null);
1306 const currentIndex = await GM.getValue('bulkCurrentIndex', 0);
1307
1308 if (campaignsJson) {
1309 const campaigns = JSON.parse(campaignsJson);
1310 const nextIndex = currentIndex + 1;
1311
1312 console.log(`Пропускаем кампанию с ошибкой. Переход к ${nextIndex + 1} из ${campaigns.length}`);
1313
1314 if (nextIndex < campaigns.length) {
1315 // Сохраняем новый индекс
1316 await GM.setValue('bulkCurrentIndex', nextIndex);
1317
1318 // Открываем следующую кампанию в новой вкладке
1319 console.log(`Открытие кампании ${nextIndex + 1}: ${campaigns[nextIndex].name}`);
1320 await GM.openInTab(campaigns[nextIndex].url, false);
1321
1322 // Закрываем текущую вкладку
1323 await wait(1000);
1324 window.close();
1325 } else {
1326 // Все кампании обработаны
1327 console.log('Все кампании обработаны, очищаем данные');
1328 await GM.deleteValue('bulkProcessingDRR');
1329 await GM.deleteValue('bulkCampaigns');
1330 await GM.deleteValue('bulkCurrentIndex');
1331
1332 // Закрываем текущую вкладку
1333 showStatus('✅ Все кампании обработаны! Закрытие вкладки...');
1334 await wait(2000);
1335 window.close();
1336 }
1337 }
1338 }
1339 }
1340
1341 // Инициализация
1342 function init() {
1343 console.log('Инициализация расширения');
1344
1345 // Проверяем, на какой странице мы находимся
1346 if (window.location.href.includes('/advert/campaigns')) {
1347 // Страница со списком кампаний
1348 console.log('Страница списка кампаний обнаружена');
1349
1350 setTimeout(() => {
1351 if (document.body) {
1352 createBulkUI();
1353 } else {
1354 console.error('Body не найден');
1355 }
1356 }, 1000);
1357 } else if (window.location.href.includes('/campaigns/auto-campaigns/') && window.location.href.includes('/campaign')) {
1358 // Страница отдельной кампании
1359 console.log('Страница кампании обнаружена');
1360
1361 // Создаем UI с небольшой задержкой, чтобы убедиться, что страница загружена
1362 setTimeout(() => {
1363 if (document.body) {
1364 createUI();
1365 } else {
1366 console.error('Body не найден');
1367 }
1368 }, 1000);
1369 } else {
1370 console.log('Не на странице кампании, UI не создается');
1371 }
1372 }
1373
1374 // Запускаем инициализацию
1375 init();
1376
1377})();