Ozon Messenger Bulk Sender

Массовая рассылка сообщений покупателям в Ozon Messenger

Size

31.0 KB

Version

1.1.25

Created

Jan 26, 2026

Updated

22 days ago

1// ==UserScript==
2// @name		Ozon Messenger Bulk Sender
3// @description		Массовая рассылка сообщений покупателям в Ozon Messenger
4// @version		1.1.25
5// @match		*://seller.ozon.ru/app/messenger/*
6// @grant		GM.getValue
7// @grant		GM.setValue
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    console.log('Ozon Messenger Bulk Sender загружен - ВЕРСИЯ 1.1.6');
13
14    // Глобальные переменные для управления процессом
15    let isRunning = false;
16    let isPaused = false;
17    let shouldStop = false;
18
19    // Функция для создания модального окна
20    function createModal() {
21        const modalHTML = `
22            <div id="bulkSenderModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 10000; justify-content: center; align-items: center;">
23                <div style="background: white; padding: 30px; border-radius: 12px; width: 500px; max-width: 90%; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
24                    <h2 style="margin: 0 0 20px 0; color: #333; font-size: 24px;">Массовая рассылка</h2>
25                    
26                    <div style="margin-bottom: 20px;">
27                        <label style="display: block; margin-bottom: 8px; color: #555; font-weight: 600;">Текст сообщения:</label>
28                        <textarea id="messageText" style="width: 100%; height: 120px; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; font-family: inherit; resize: vertical;" placeholder="Введите текст сообщения..."></textarea>
29                    </div>
30                    
31                    <div style="margin-bottom: 25px;">
32                        <label style="display: block; margin-bottom: 8px; color: #555; font-weight: 600;">Дата последнего сообщения (до какой даты отправляли):</label>
33                        <input type="date" id="filterDate" style="width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;">
34                        <small style="color: #777; display: block; margin-top: 5px;">Будут обработаны чаты с датой последнего сообщения до указанной включительно</small>
35                    </div>
36                    
37                    <div style="margin-bottom: 20px;">
38                        <label style="display: flex; align-items: center; cursor: pointer;">
39                            <input type="checkbox" id="testMode" checked style="width: 18px; height: 18px; margin-right: 10px; cursor: pointer;">
40                            <span style="color: #555; font-weight: 600;">Тестовый режим (отправить только 1 сообщение)</span>
41                        </label>
42                        <small style="color: #ff6600; display: block; margin-top: 5px; margin-left: 28px;">⚠️ Рекомендуется для первого запуска</small>
43                    </div>
44                    
45                    <div id="statusBlock" style="display: none; margin-bottom: 20px; padding: 15px; background: #f0f8ff; border-radius: 6px; border-left: 4px solid #0066cc;">
46                        <div style="font-weight: 600; color: #0066cc; margin-bottom: 5px;">Статус:</div>
47                        <div id="statusText" style="color: #333;">Готов к запуску</div>
48                        <div id="progressText" style="color: #666; margin-top: 5px; font-size: 13px;"></div>
49                    </div>
50                    
51                    <div style="display: flex; gap: 10px; justify-content: flex-end;">
52                        <button id="startButton" style="padding: 12px 24px; background: #0066cc; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600;">Запустить</button>
53                        <button id="pauseButton" style="display: none; padding: 12px 24px; background: #ff9800; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600;">Пауза</button>
54                        <button id="stopButton" style="display: none; padding: 12px 24px; background: #f44336; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600;">Стоп</button>
55                        <button id="closeButton" style="padding: 12px 24px; background: #999; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600;">Закрыть</button>
56                    </div>
57                </div>
58            </div>
59        `;
60        
61        document.body.insertAdjacentHTML('beforeend', modalHTML);
62        console.log('Модальное окно создано');
63        
64        // Обработчики событий
65        document.getElementById('closeButton').addEventListener('click', closeModal);
66        document.getElementById('startButton').addEventListener('click', () => {
67            console.log('!!! КЛИК ПО КНОПКЕ ЗАПУСТИТЬ !!!');
68            startBulkSending();
69        });
70        document.getElementById('pauseButton').addEventListener('click', togglePause);
71        document.getElementById('stopButton').addEventListener('click', stopBulkSending);
72    }
73
74    // Функция для открытия модального окна
75    function openModal() {
76        const modal = document.getElementById('bulkSenderModal');
77        if (modal) {
78            modal.style.display = 'flex';
79            console.log('Модальное окно открыто');
80        }
81    }
82
83    // Функция для закрытия модального окна
84    function closeModal() {
85        const modal = document.getElementById('bulkSenderModal');
86        if (modal && !isRunning) {
87            modal.style.display = 'none';
88            console.log('Модальное окно закрыто');
89        } else if (isRunning) {
90            alert('Дождитесь завершения рассылки или нажмите "Стоп"');
91        }
92    }
93
94    // Функция для обновления статуса
95    function updateStatus(status, progress = '') {
96        const statusBlock = document.getElementById('statusBlock');
97        const statusText = document.getElementById('statusText');
98        const progressText = document.getElementById('progressText');
99        
100        if (statusBlock && statusText) {
101            statusBlock.style.display = 'block';
102            statusText.textContent = status;
103            progressText.textContent = progress;
104            console.log('Статус:', status, progress);
105        }
106    }
107
108    // Функция для парсинга даты из текста
109    function parseDate(dateText) {
110        console.log('Парсинг даты:', dateText);
111        
112        // Если дата в формате "24 января" или "21 июня 2025"
113        const months = {
114            'января': 0, 'февраля': 1, 'марта': 2, 'апреля': 3,
115            'мая': 4, 'июня': 5, 'июля': 6, 'августа': 7,
116            'сентября': 8, 'октября': 9, 'ноября': 10, 'декабря': 11
117        };
118        
119        const parts = dateText.trim().split(' ');
120        
121        // Формат "21 июня 2025"
122        if (parts.length === 3) {
123            const day = parseInt(parts[0]);
124            const month = months[parts[1].toLowerCase()];
125            const year = parseInt(parts[2]);
126            
127            if (!isNaN(day) && month !== undefined && !isNaN(year)) {
128                const date = new Date(year, month, day);
129                console.log('Распознанная дата:', date);
130                return date;
131            }
132        }
133        
134        // Формат "24 января"
135        if (parts.length === 2) {
136            const day = parseInt(parts[0]);
137            const month = months[parts[1].toLowerCase()];
138            
139            if (!isNaN(day) && month !== undefined) {
140                const currentYear = new Date().getFullYear();
141                const date = new Date(currentYear, month, day);
142                console.log('Распознанная дата:', date);
143                return date;
144            }
145        }
146        
147        console.log('Не удалось распознать дату');
148        return null;
149    }
150
151    // Функция для получения всех чатов
152    function getAllChats() {
153        // Сначала проверяем, есть ли результаты поиска (класс index_chat_EHlBq)
154        const searchChats = document.querySelectorAll('.index_chat_EHlBq');
155        
156        if (searchChats.length > 0) {
157            // Если есть результаты поиска, используем их
158            console.log('Найдены результаты поиска:', searchChats.length);
159            return Array.from(searchChats);
160        }
161        
162        // Если поиска нет, используем обычные чаты
163        const chats = document.querySelectorAll('.index_chat_4fr82');
164        // Фильтруем только видимые чаты (учитываем фильтр поиска)
165        const visibleChats = Array.from(chats).filter(chat => {
166            const style = window.getComputedStyle(chat);
167            // Проверяем, что чат не скрыт (убрали проверку высоты, т.к. чаты виртуализированы)
168            return style.display !== 'none' && 
169                   style.visibility !== 'hidden' && 
170                   style.opacity !== '0' &&
171                   chat.offsetParent !== null; // Проверяем offsetParent для фильтрации
172        });
173        console.log('Найдено чатов:', visibleChats.length, '(всего в DOM:', chats.length, ')');
174        return visibleChats;
175    }
176
177    // Функция для прокрутки списка чатов и загрузки всех чатов
178    async function loadAllChats() {
179        const chatListContainer = document.querySelector('.om_1_f0');
180        if (!chatListContainer) {
181            console.error('Контейнер списка чатов не найден');
182            return;
183        }
184        
185        console.log('Начинаем загрузку всех чатов...');
186        let previousCount = 0;
187        let currentCount = document.querySelectorAll('.index_chat_4fr82').length;
188        let attempts = 0;
189        const maxAttempts = 50; // Максимум 50 попыток прокрутки
190        
191        while (attempts < maxAttempts) {
192            // Прокручиваем вниз
193            chatListContainer.scrollTop = chatListContainer.scrollHeight;
194            await sleep(500); // Ждем загрузки новых чатов
195            
196            currentCount = document.querySelectorAll('.index_chat_4fr82').length;
197            console.log(`Попытка ${attempts + 1}: загружено ${currentCount} чатов`);
198            
199            // Если количество чатов не изменилось, значит все загружены
200            if (currentCount === previousCount) {
201                console.log('Все чаты загружены');
202                break;
203            }
204            
205            previousCount = currentCount;
206            attempts++;
207        }
208        
209        // Прокручиваем обратно наверх
210        chatListContainer.scrollTop = 0;
211        await sleep(500);
212        
213        console.log(`Загрузка завершена. Всего чатов: ${currentCount}`);
214        return currentCount;
215    }
216
217    // Функция для клика по чату
218    async function clickChat(chat) {
219        // Пробуем оба селектора для имени чата (обычный и поисковый)
220        const chatName = chat.querySelector('.index_chatTitle_TiXTq')?.textContent || 
221                        chat.querySelector('.index_chatTitle_x9txX')?.textContent || 
222                        'Неизвестно';
223        console.log('Клик по чату:', chatName);
224        chat.click();
225        await sleep(2000); // Ждем загрузки чата
226    }
227
228    // Функция для получения даты последнего сообщения в открытом чате
229    function getLastMessageDate() {
230        const dateElements = document.querySelectorAll('.om_1_n4');
231        if (dateElements.length > 0) {
232            // Берем последнюю дату (последнее сообщение)
233            const lastDateElement = dateElements[dateElements.length - 1];
234            const dateText = lastDateElement.textContent.trim();
235            console.log('Дата последнего сообщения:', dateText);
236            return parseDate(dateText);
237        }
238        console.log('Элемент даты не найден');
239        return null;
240    }
241
242    // Функция для отправки сообщения
243    async function sendMessage(messageText) {
244        console.log('Отправка сообщения:', messageText);
245        
246        // Находим текстовое поле
247        const textarea = document.querySelector('.om_17_a4');
248        if (!textarea) {
249            console.error('Текстовое поле не найдено');
250            return false;
251        }
252        
253        // Вставляем текст
254        textarea.value = messageText;
255        textarea.dispatchEvent(new Event('input', { bubbles: true }));
256        console.log('Текст вставлен в поле');
257        
258        // Ждем 1 секунду
259        await sleep(1000);
260        
261        // Находим кнопку отправки (вторая кнопка с классом om_17_a8)
262        const sendButtons = document.querySelectorAll('.om_17_a8');
263        if (sendButtons.length < 2) {
264            console.error('Кнопка отправки не найдена');
265            return false;
266        }
267        
268        // Кликаем на вторую кнопку (первая - это прикрепление файла)
269        const sendButton = sendButtons[1];
270        sendButton.click();
271        console.log('Сообщение отправлено (клик выполнен)');
272        
273        // Ждем 1 секунду после отправки
274        await sleep(1000);
275        
276        return true;
277    }
278
279    // Функция задержки
280    function sleep(ms) {
281        return new Promise(resolve => setTimeout(resolve, ms));
282    }
283
284    // Основная функция рассылки
285    async function startBulkSending() {
286        console.log('=== ФУНКЦИЯ startBulkSending ВЫЗВАНА ===');
287        
288        const messageText = document.getElementById('messageText').value.trim();
289        const filterDateInput = document.getElementById('filterDate').value;
290        const testModeCheckbox = document.getElementById('testMode');
291        const isTestMode = testModeCheckbox ? testModeCheckbox.checked : false;
292        
293        console.log('Текст сообщения:', messageText);
294        console.log('Дата фильтра:', filterDateInput);
295        console.log('Тестовый режим:', isTestMode);
296        
297        if (!messageText) {
298            alert('Введите текст сообщения');
299            console.log('Остановка: нет текста сообщения');
300            return;
301        }
302        
303        if (!filterDateInput) {
304            alert('Выберите дату фильтра');
305            console.log('Остановка: нет даты фильтра');
306            return;
307        }
308        
309        const filterDate = new Date(filterDateInput);
310        console.log('Начало рассылки. Фильтр по дате:', filterDate);
311        console.log('Тестовый режим:', isTestMode ? 'ДА (только 1 сообщение)' : 'НЕТ (все подходящие чаты)');
312        
313        // Меняем интерфейс
314        isRunning = true;
315        isPaused = false;
316        shouldStop = false;
317        
318        document.getElementById('startButton').style.display = 'none';
319        document.getElementById('pauseButton').style.display = 'inline-block';
320        document.getElementById('stopButton').style.display = 'inline-block';
321        document.getElementById('messageText').disabled = true;
322        document.getElementById('filterDate').disabled = true;
323        if (testModeCheckbox) testModeCheckbox.disabled = true;
324        
325        updateStatus('Запуск рассылки...', isTestMode ? 'Тестовый режим: будет отправлено только 1 сообщение' : '');
326        
327        const chats = getAllChats();
328        let processed = 0;
329        let sent = 0;
330        let skipped = 0;
331        
332        for (let i = 0; i < chats.length; i++) {
333            // Проверка на паузу
334            while (isPaused && !shouldStop) {
335                updateStatus('Пауза', `Обработано: ${processed}/${chats.length}, Отправлено: ${sent}, Пропущено: ${skipped}`);
336                await sleep(500);
337            }
338            
339            // Проверка на остановку
340            if (shouldStop) {
341                updateStatus('Остановлено пользователем', `Обработано: ${processed}/${chats.length}, Отправлено: ${sent}, Пропущено: ${skipped}`);
342                break;
343            }
344            
345            // Проверка тестового режима - останавливаемся после первого отправленного сообщения
346            if (isTestMode && sent >= 1) {
347                console.log('Тестовый режим: достигнут лимит в 1 сообщение, останавливаем рассылку');
348                updateStatus('Тестовая рассылка завершена!', `Отправлено: ${sent} сообщение (тестовый режим)`);
349                break;
350            }
351            
352            const chat = chats[i];
353            const chatName = chat.querySelector('.index_chatTitle_TiXTq')?.textContent || 
354                           chat.querySelector('.index_chatTitle_x9txX')?.textContent || 
355                           'Неизвестно';
356            
357            updateStatus(`Обработка чата: ${chatName}`, `Прогресс: ${i + 1}/${chats.length}, Отправлено: ${sent}, Пропущено: ${skipped}`);
358            
359            // Кликаем по чату
360            await clickChat(chat);
361            
362            // Получаем дату последнего сообщения
363            const lastMessageDate = getLastMessageDate();
364            
365            if (!lastMessageDate) {
366                console.log('Не удалось получить дату, пропускаем чат');
367                skipped++;
368                processed++;
369                continue;
370            }
371            
372            // Проверяем дату
373            if (lastMessageDate <= filterDate) {
374                console.log(`Дата подходит (${lastMessageDate.toLocaleDateString()} <= ${filterDate.toLocaleDateString()}), отправляем сообщение`);
375                
376                const success = await sendMessage(messageText);
377                if (success) {
378                    sent++;
379                    console.log(`Сообщение отправлено в чат: ${chatName}`);
380                } else {
381                    console.error(`Ошибка отправки в чат: ${chatName}`);
382                    skipped++;
383                }
384            } else {
385                console.log(`Дата не подходит (${lastMessageDate.toLocaleDateString()} > ${filterDate.toLocaleDateString()}), пропускаем`);
386                skipped++;
387            }
388            
389            processed++;
390            
391            // Небольшая пауза между чатами
392            await sleep(1000);
393        }
394        
395        // Завершение
396        isRunning = false;
397        isPaused = false;
398        
399        document.getElementById('startButton').style.display = 'inline-block';
400        document.getElementById('pauseButton').style.display = 'none';
401        document.getElementById('stopButton').style.display = 'none';
402        document.getElementById('messageText').disabled = false;
403        document.getElementById('filterDate').disabled = false;
404        if (testModeCheckbox) testModeCheckbox.disabled = false;
405        
406        updateStatus('Рассылка завершена!', `Всего обработано: ${processed}, Отправлено: ${sent}, Пропущено: ${skipped}`);
407        console.log('Рассылка завершена');
408    }
409
410    // Функция паузы/возобновления
411    function togglePause() {
412        isPaused = !isPaused;
413        const pauseButton = document.getElementById('pauseButton');
414        
415        if (isPaused) {
416            pauseButton.textContent = 'Продолжить';
417            pauseButton.style.background = '#4caf50';
418            console.log('Рассылка приостановлена');
419        } else {
420            pauseButton.textContent = 'Пауза';
421            pauseButton.style.background = '#ff9800';
422            console.log('Рассылка возобновлена');
423        }
424    }
425
426    // Функция остановки
427    function stopBulkSending() {
428        if (confirm('Вы уверены, что хотите остановить рассылку?')) {
429            shouldStop = true;
430            isPaused = false;
431            console.log('Запрошена остановка рассылки');
432        }
433    }
434
435    // Функция для создания плавающей кнопки
436    function createFloatingButton() {
437        // Проверяем, не создана ли уже кнопка
438        if (document.getElementById('bulkSenderFloatingBtn')) {
439            return;
440        }
441        
442        const button = document.createElement('button');
443        button.id = 'bulkSenderFloatingBtn';
444        button.textContent = '📧 Рассылка';
445        button.style.cssText = `
446            position: fixed;
447            bottom: 20px;
448            right: 20px;
449            padding: 15px 25px;
450            background: #0066cc;
451            color: white;
452            border: none;
453            border-radius: 50px;
454            cursor: pointer;
455            font-size: 16px;
456            font-weight: 600;
457            box-shadow: 0 4px 12px rgba(0, 102, 204, 0.4);
458            z-index: 9999;
459            transition: all 0.3s;
460        `;
461        
462        button.addEventListener('mouseenter', () => {
463            button.style.background = '#0052a3';
464            button.style.transform = 'scale(1.05)';
465            button.style.boxShadow = '0 6px 16px rgba(0, 102, 204, 0.6)';
466        });
467        
468        button.addEventListener('mouseleave', () => {
469            button.style.background = '#0066cc';
470            button.style.transform = 'scale(1)';
471            button.style.boxShadow = '0 4px 12px rgba(0, 102, 204, 0.4)';
472        });
473        
474        button.addEventListener('click', openModal);
475        
476        document.body.appendChild(button);
477        console.log('Плавающая кнопка создана');
478    }
479
480    // Функция для создания фильтра по дате в списке чатов
481    function createDateFilter() {
482        // Проверяем, не создан ли уже фильтр
483        if (document.getElementById('dateFilterContainer')) {
484            return;
485        }
486        
487        // Ищем контейнер с поиском
488        const searchContainer = document.querySelector('.om_1_f0')?.parentElement;
489        if (!searchContainer) {
490            console.log('Контейнер для фильтра не найден');
491            return;
492        }
493        
494        // Создаем контейнер для фильтра
495        const filterContainer = document.createElement('div');
496        filterContainer.id = 'dateFilterContainer';
497        filterContainer.style.cssText = `
498            padding: 15px;
499            background: #f5f5f5;
500            border-bottom: 1px solid #ddd;
501            display: flex;
502            gap: 10px;
503            align-items: center;
504            flex-wrap: wrap;
505        `;
506        
507        filterContainer.innerHTML = `
508            <label style="font-size: 13px; font-weight: 600; color: #555;">Фильтр по дате последнего сообщения:</label>
509            <input type="date" id="dateFilterFrom" style="padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px;">
510            <span style="color: #555;"></span>
511            <input type="date" id="dateFilterTo" style="padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 13px;">
512            <button id="applyDateFilter" style="padding: 8px 16px; background: #0066cc; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: 600;">Применить</button>
513            <button id="clearDateFilter" style="padding: 8px 16px; background: #999; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: 600; display: none;">Сбросить</button>
514        `;
515        
516        // Вставляем фильтр перед списком чатов
517        const chatList = document.querySelector('.om_1_f0');
518        if (chatList && chatList.parentElement) {
519            chatList.parentElement.insertBefore(filterContainer, chatList);
520            console.log('Фильтр по дате создан');
521            
522            // Обработчики событий
523            document.getElementById('applyDateFilter').addEventListener('click', applyDateFilter);
524            document.getElementById('clearDateFilter').addEventListener('click', clearDateFilter);
525        }
526    }
527
528    // Функция применения фильтра по дате
529    async function applyDateFilter() {
530        const dateFrom = document.getElementById('dateFilterFrom').value;
531        const dateTo = document.getElementById('dateFilterTo').value;
532        
533        if (!dateFrom || !dateTo) {
534            alert('Выберите обе даты для фильтрации');
535            return;
536        }
537        
538        const filterFrom = new Date(dateFrom);
539        const filterTo = new Date(dateTo);
540        
541        console.log('Применение фильтра по дате:', filterFrom, '-', filterTo);
542        
543        // Показываем индикатор загрузки
544        const applyButton = document.getElementById('applyDateFilter');
545        const originalText = applyButton.textContent;
546        applyButton.textContent = 'Фильтрация...';
547        applyButton.disabled = true;
548        
549        // Получаем все чаты
550        const allChats = document.querySelectorAll('.index_chat_4fr82');
551        let visibleCount = 0;
552        let hiddenCount = 0;
553        
554        allChats.forEach(chat => {
555            // Ищем дату в самом элементе чата (не открывая его)
556            const dateElement = chat.querySelector('.index_chatDate_z4mNc, .index_chatDate_WJ-\\+mb');
557            
558            if (dateElement) {
559                const dateText = dateElement.textContent.trim();
560                const chatDate = parseDate(dateText);
561                
562                if (chatDate) {
563                    // Проверяем, попадает ли дата в диапазон
564                    if (chatDate >= filterFrom && chatDate <= filterTo) {
565                        // Дата подходит - оставляем чат видимым
566                        chat.style.display = 'grid';
567                        visibleCount++;
568                        console.log('Чат подходит:', chat.querySelector('.index_chatTitle_TiXTq')?.textContent, chatDate);
569                    } else {
570                        // Дата не подходит - скрываем чат
571                        chat.style.display = 'none';
572                        hiddenCount++;
573                    }
574                } else {
575                    // Не удалось распознать дату - оставляем видимым
576                    chat.style.display = 'grid';
577                    visibleCount++;
578                }
579            } else {
580                // Нет даты - оставляем видимым
581                chat.style.display = 'grid';
582                visibleCount++;
583            }
584        });
585        
586        // Восстанавливаем кнопку
587        applyButton.textContent = originalText;
588        applyButton.disabled = false;
589        
590        // Показываем кнопку сброса
591        document.getElementById('clearDateFilter').style.display = 'inline-block';
592        
593        console.log(`Фильтр применен. Показано: ${visibleCount}, Скрыто: ${hiddenCount}`);
594        alert(`Фильтр применен!\nПоказано чатов: ${visibleCount}\nСкрыто чатов: ${hiddenCount}`);
595    }
596
597    // Функция сброса фильтра
598    function clearDateFilter() {
599        // Показываем все чаты
600        const allChats = document.querySelectorAll('.index_chat_4fr82');
601        allChats.forEach(chat => {
602            chat.style.display = 'grid';
603        });
604        
605        // Очищаем поля
606        document.getElementById('dateFilterFrom').value = '';
607        document.getElementById('dateFilterTo').value = '';
608        
609        // Скрываем кнопку сброса
610        document.getElementById('clearDateFilter').style.display = 'none';
611        
612        console.log('Фильтр сброшен');
613    }
614
615    // Инициализация
616    function init() {
617        console.log('Инициализация расширения');
618        
619        // Проверяем, что мы на странице мессенджера с покупателями
620        if (!window.location.href.includes('group=customers_v2')) {
621            console.log('Не на странице чатов с покупателями, расширение не активно');
622            return;
623        }
624        
625        console.log('Страница подходит, создаем интерфейс');
626        
627        // Создаем плавающую кнопку сразу
628        createFloatingButton();
629        createModal();
630        
631        // Ждем загрузки списка чатов и создаем фильтр
632        setTimeout(() => {
633            createDateFilter();
634        }, 2000);
635        
636        console.log('Интерфейс создан');
637    }
638
639    // Запуск при загрузке страницы
640    if (document.readyState === 'loading') {
641        document.addEventListener('DOMContentLoaded', init);
642    } else {
643        init();
644    }
645})();
Ozon Messenger Bulk Sender | Robomonkey