Ozon Messenger Bulk Sender

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

Size

65.3 KB

Version

1.1.39

Created

Jan 26, 2026

Updated

3 months ago

1// ==UserScript==
2// @name		Ozon Messenger Bulk Sender
3// @description		Массовая рассылка сообщений покупателям в Ozon Messenger
4// @version		1.1.39
5// @match		*://seller.ozon.ru/*
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    
21    // Загрузка базы данных контактов
22    async function loadContactsDatabase() {
23        try {
24            const dbString = await GM.getValue('contactsDatabase', null);
25            if (dbString) {
26                const db = JSON.parse(dbString);
27                console.log('База данных загружена:', Object.keys(db.contacts || {}).length, 'контактов');
28                return db;
29            }
30        } catch (error) {
31            console.error('Ошибка загрузки базы данных:', error);
32        }
33        
34        // Возвращаем пустую базу, если её нет
35        return {
36            contacts: {},
37            lastUpdate: null,
38            totalContacts: 0
39        };
40    }
41    
42    // Сохранение базы данных контактов
43    async function saveContactsDatabase(db) {
44        try {
45            db.lastUpdate = new Date().toISOString();
46            db.totalContacts = Object.keys(db.contacts).length;
47            await GM.setValue('contactsDatabase', JSON.stringify(db));
48            console.log('База данных сохранена:', db.totalContacts, 'контактов');
49            return true;
50        } catch (error) {
51            console.error('Ошибка сохранения базы данных:', error);
52            return false;
53        }
54    }
55    
56    // Добавление контакта в базу
57    function addContactToDatabase(db, contactId, contactData) {
58        db.contacts[contactId] = {
59            id: contactId,
60            name: contactData.name || 'Неизвестно',
61            lastMessageDate: contactData.lastMessageDate || null,
62            lastSentDate: contactData.lastSentDate || null,
63            addedAt: contactData.addedAt || new Date().toISOString(),
64            messageCount: contactData.messageCount || 0
65        };
66    }
67    
68    // Получение статистики базы данных
69    function getDatabaseStats(db) {
70        const total = Object.keys(db.contacts).length;
71        const withDates = Object.values(db.contacts).filter(c => c.lastMessageDate).length;
72        const sent = Object.values(db.contacts).filter(c => c.lastSentDate).length;
73        
74        return {
75            total,
76            withDates,
77            sent,
78            notSent: total - sent,
79            lastUpdate: db.lastUpdate
80        };
81    }
82    
83    // Очистка базы данных
84    async function clearContactsDatabase() {
85        if (confirm('Вы уверены, что хотите удалить всю базу контактов? Это действие нельзя отменить.')) {
86            await GM.setValue('contactsDatabase', null);
87            console.log('База данных очищена');
88            return true;
89        }
90        return false;
91    }
92    
93    // Удаление дубликатов из базы данных
94    async function removeDuplicatesFromDatabase() {
95        const db = await loadContactsDatabase();
96        const initialCount = Object.keys(db.contacts).length;
97        
98        // Дубликаты уже не могут существовать, т.к. мы используем ID как ключ
99        // Но проверим на всякий случай
100        console.log('Проверка дубликатов в базе данных...');
101        console.log('Всего контактов:', initialCount);
102        
103        // Дубликатов быть не может, т.к. используется объект с уникальными ключами
104        alert(`Проверка завершена!\nВсего контактов: ${initialCount}\nДубликатов не обнаружено (используются уникальные ID)`);
105        
106        return {
107            initial: initialCount,
108            final: initialCount,
109            removed: 0
110        };
111    }
112    
113    // Функция для создания модального окна просмотра базы
114    function createViewDatabaseModal() {
115        // Проверяем, не создано ли уже окно
116        if (document.getElementById('viewDbModal')) {
117            return;
118        }
119        
120        const modalHTML = `
121            <div id="viewDbModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 10001; justify-content: center; align-items: center;">
122                <div style="background: white; padding: 30px; border-radius: 12px; width: 800px; max-width: 95%; max-height: 90vh; overflow-y: auto; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
123                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
124                        <h2 style="margin: 0; color: #333; font-size: 24px;">👁️ Просмотр базы контактов</h2>
125                        <button id="closeViewDbButton" style="padding: 8px 16px; background: #999; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 600;">Закрыть</button>
126                    </div>
127                    
128                    <div id="viewDbStatsBlock" style="margin-bottom: 20px; padding: 15px; background: #f0f8ff; border-radius: 6px; border-left: 4px solid #0066cc;">
129                        <div style="font-weight: 600; color: #0066cc; margin-bottom: 10px;">Статистика базы:</div>
130                        <div id="viewDbStatsContent" style="color: #333; font-size: 14px;"></div>
131                    </div>
132                    
133                    <div style="margin-bottom: 15px; display: flex; gap: 10px; align-items: center;">
134                        <input type="text" id="searchContactInput" placeholder="Поиск по имени..." style="flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;">
135                        <button id="removeDuplicatesButton" style="padding: 10px 18px; background: #ff9800; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">🗑️ Удалить дубликаты</button>
136                    </div>
137                    
138                    <div id="contactsListContainer" style="max-height: 500px; overflow-y: auto; border: 1px solid #ddd; border-radius: 6px;">
139                        <div id="contactsList" style="padding: 10px;"></div>
140                    </div>
141                </div>
142            </div>
143        `;
144        
145        document.body.insertAdjacentHTML('beforeend', modalHTML);
146        console.log('Модальное окно просмотра базы создано');
147        
148        // Обработчики событий
149        document.getElementById('closeViewDbButton').addEventListener('click', closeViewDatabaseModal);
150        document.getElementById('removeDuplicatesButton').addEventListener('click', async () => {
151            const result = await removeDuplicatesFromDatabase();
152            await loadAndDisplayContacts();
153        });
154        
155        // Поиск по контактам
156        document.getElementById('searchContactInput').addEventListener('input', (e) => {
157            filterContactsList(e.target.value);
158        });
159    }
160    
161    // Функция для открытия модального окна просмотра базы
162    async function openViewDatabaseModal() {
163        createViewDatabaseModal();
164        const modal = document.getElementById('viewDbModal');
165        if (modal) {
166            modal.style.display = 'flex';
167            await loadAndDisplayContacts();
168            console.log('Модальное окно просмотра базы открыто');
169        }
170    }
171    
172    // Функция для закрытия модального окна просмотра базы
173    function closeViewDatabaseModal() {
174        const modal = document.getElementById('viewDbModal');
175        if (modal) {
176            modal.style.display = 'none';
177            console.log('Модальное окно просмотра базы закрыто');
178        }
179    }
180    
181    // Функция для загрузки и отображения контактов
182    async function loadAndDisplayContacts(searchQuery = '') {
183        const db = await loadContactsDatabase();
184        const stats = getDatabaseStats(db);
185        const contacts = Object.values(db.contacts);
186        
187        // Обновляем статистику
188        const statsContent = document.getElementById('viewDbStatsContent');
189        if (statsContent) {
190            statsContent.innerHTML = `
191                <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;">
192                    <div><strong>Всего контактов:</strong> ${stats.total}</div>
193                    <div><strong>С датами:</strong> ${stats.withDates}</div>
194                    <div><strong>Отправлено:</strong> ${stats.sent}</div>
195                    <div><strong>Не отправлено:</strong> ${stats.notSent}</div>
196                </div>
197            `;
198        }
199        
200        // Фильтруем контакты по поисковому запросу
201        const filteredContacts = searchQuery 
202            ? contacts.filter(c => c.name.toLowerCase().includes(searchQuery.toLowerCase()))
203            : contacts;
204        
205        // Сортируем по дате последнего сообщения (новые сверху)
206        filteredContacts.sort((a, b) => {
207            if (!a.lastMessageDate) return 1;
208            if (!b.lastMessageDate) return -1;
209            return new Date(b.lastMessageDate) - new Date(a.lastMessageDate);
210        });
211        
212        // Отображаем список контактов
213        const contactsList = document.getElementById('contactsList');
214        if (contactsList) {
215            if (filteredContacts.length === 0) {
216                contactsList.innerHTML = '<div style="text-align: center; padding: 40px; color: #999;">Контакты не найдены</div>';
217            } else {
218                contactsList.innerHTML = filteredContacts.map(contact => {
219                    const lastMsgDate = contact.lastMessageDate 
220                        ? new Date(contact.lastMessageDate).toLocaleDateString('ru-RU')
221                        : 'Нет данных';
222                    const lastSentDate = contact.lastSentDate 
223                        ? new Date(contact.lastSentDate).toLocaleDateString('ru-RU')
224                        : 'Не отправлялось';
225                    const sentStatus = contact.lastSentDate ? '✅' : '⏳';
226                    
227                    return `
228                        <div style="padding: 12px; margin-bottom: 8px; background: ${contact.lastSentDate ? '#f0f8ff' : '#fff8f0'}; border: 1px solid #ddd; border-radius: 6px; font-size: 13px;">
229                            <div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 8px;">
230                                <div style="font-weight: 600; color: #333; font-size: 14px;">${sentStatus} ${contact.name}</div>
231                                <div style="font-size: 11px; color: #999;">${contact.id.substring(0, 8)}...</div>
232                            </div>
233                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; color: #666;">
234                                <div><strong>Последнее сообщение:</strong> ${lastMsgDate}</div>
235                                <div><strong>Отправлено:</strong> ${lastSentDate}</div>
236                            </div>
237                            ${contact.messageCount > 0 ? `<div style="margin-top: 5px; color: #666;"><strong>Отправлено сообщений:</strong> ${contact.messageCount}</div>` : ''}
238                        </div>
239                    `;
240                }).join('');
241            }
242        }
243    }
244    
245    // Функция для фильтрации списка контактов
246    function filterContactsList(searchQuery) {
247        loadAndDisplayContacts(searchQuery);
248    }
249
250    // ============= ФУНКЦИИ ДЛЯ СБОРА КОНТАКТОВ =============
251    
252    // Функция для извлечения ID из URL чата
253    function extractChatId(chatElement) {
254        // Ищем атрибут deeplink в элементе чата
255        const deeplink = chatElement.getAttribute('deeplink');
256        if (deeplink) {
257            const match = deeplink.match(/id=([a-f0-9-]+)/);
258            if (match) {
259                console.log('Извлечен ID из deeplink:', match[1]);
260                return match[1];
261            }
262        }
263        
264        // Запасной вариант - ищем ссылку внутри элемента
265        const link = chatElement.querySelector('a[href*="id="]');
266        if (link) {
267            const href = link.getAttribute('href');
268            const match = href.match(/id=([a-f0-9-]+)/);
269            if (match) {
270                console.log('Извлечен ID из ссылки:', match[1]);
271                return match[1];
272            }
273        }
274        
275        console.log('Не удалось извлечь ID чата');
276        return null;
277    }
278    
279    // Функция для получения данных чата из элемента списка
280    function getChatDataFromElement(chatElement) {
281        const chatName = chatElement.querySelector('.index_chatTitle_TiXTq')?.textContent?.trim() || 'Неизвестно';
282        const dateElement = chatElement.querySelector('.index_chatDate_z4mNc, .index_chatDate_WJ-\\+mb');
283        const dateText = dateElement?.textContent?.trim();
284        const lastMessageDate = dateText ? parseDate(dateText) : null;
285        
286        return {
287            name: chatName,
288            lastMessageDate: lastMessageDate ? lastMessageDate.toISOString() : null,
289            lastSentDate: null,
290            messageCount: 0
291        };
292    }
293    
294    // Функция для скролла списка чатов
295    async function scrollChatList() {
296        const chatListContainer = document.querySelector('.om_1_f0');
297        if (!chatListContainer) {
298            console.error('Контейнер списка чатов не найден');
299            return false;
300        }
301        
302        // Скроллим вниз
303        chatListContainer.scrollTop = chatListContainer.scrollHeight;
304        console.log('Скролл выполнен, scrollTop:', chatListContainer.scrollTop);
305        
306        // Ждем подгрузки новых чатов - увеличиваем время ожидания
307        await sleep(2500);
308        return true;
309    }
310    
311    // Основная функция сбора базы контактов
312    async function collectContactsDatabase(updateExisting = false, testMode = false) {
313        console.log('=== НАЧАЛО СБОРА БАЗЫ КОНТАКТОВ ===');
314        console.log('Режим:', updateExisting ? 'Обновление существующей базы' : 'Полный сбор');
315        console.log('Тестовый режим:', testMode ? 'ДА (только 1 контакт)' : 'НЕТ');
316        
317        // Загружаем существующую базу
318        const db = await loadContactsDatabase();
319        const initialCount = Object.keys(db.contacts).length;
320        console.log('Начальное количество контактов в базе:', initialCount);
321        
322        // Трекер обработанных ID в этой сессии
323        const processedInSession = new Set();
324        
325        let newContacts = 0;
326        let updatedContacts = 0;
327        let scrollAttempts = 0;
328        let previousChatCount = 0;
329        let noNewChatsCount = 0;
330        const maxNoNewChatsAttempts = 3; // Если 3 раза подряд не появились новые чаты - останавливаемся
331        
332        while (!shouldStop) {
333            // Проверка на паузу
334            while (isPaused && !shouldStop) {
335                updateStatus('Пауза сбора базы', `Новых: ${newContacts}, Обновлено: ${updatedContacts}`);
336                await sleep(500);
337            }
338            
339            if (shouldStop) {
340                console.log('Сбор базы остановлен пользователем');
341                break;
342            }
343            
344            // Получаем текущие чаты
345            const currentChats = getAllChats();
346            console.log(`Попытка ${scrollAttempts + 1}: найдено ${currentChats.length} чатов в DOM`);
347            
348            // Обрабатываем каждый чат
349            for (const chatElement of currentChats) {
350                const chatId = extractChatId(chatElement);
351                
352                if (!chatId) {
353                    console.log('Не удалось извлечь ID чата, пропускаем');
354                    continue;
355                }
356                
357                // Пропускаем, если уже обработали в этой сессии
358                if (processedInSession.has(chatId)) {
359                    continue;
360                }
361                
362                // Проверяем, есть ли уже этот контакт в базе
363                const existsInDb = db.contacts[chatId] !== undefined;
364                
365                // В режиме обновления - пропускаем контакты, которых нет в базе
366                if (updateExisting && !existsInDb) {
367                    console.log(`Режим обновления: контакт ${chatId} не в базе, пропускаем`);
368                    continue;
369                }
370                
371                // В режиме полного сбора - пропускаем контакты, которые уже есть
372                if (!updateExisting && existsInDb) {
373                    console.log(`Режим полного сбора: контакт ${chatId} уже в базе, пропускаем`);
374                    continue;
375                }
376                
377                // Получаем данные чата
378                const chatData = getChatDataFromElement(chatElement);
379                
380                if (existsInDb) {
381                    // Обновляем существующий контакт
382                    db.contacts[chatId].name = chatData.name;
383                    db.contacts[chatId].lastMessageDate = chatData.lastMessageDate;
384                    updatedContacts++;
385                    console.log(`Обновлен контакт: ${chatData.name} (${chatId})`);
386                } else {
387                    // Добавляем новый контакт
388                    addContactToDatabase(db, chatId, chatData);
389                    newContacts++;
390                    console.log(`Добавлен контакт: ${chatData.name} (${chatId})`);
391                }
392                
393                // Отмечаем как обработанный в этой сессии
394                processedInSession.add(chatId);
395                
396                // Проверка тестового режима - останавливаемся после первого собранного контакта
397                if (testMode && (newContacts >= 1 || updatedContacts >= 1)) {
398                    console.log('Тестовый режим: собран 1 контакт, останавливаем сбор');
399                    shouldStop = true;
400                    break;
401                }
402            }
403            
404            // Обновляем статус
405            updateStatus(
406                'Сбор базы контактов...',
407                `Новых: ${newContacts} | Обновлено: ${updatedContacts} | Попыток скролла: ${scrollAttempts}`
408            );
409            
410            // Если тестовый режим и уже собрали контакт - выходим
411            if (testMode && (newContacts >= 1 || updatedContacts >= 1)) {
412                break;
413            }
414            
415            // Проверяем, появились ли новые чаты после скролла
416            if (currentChats.length === previousChatCount) {
417                noNewChatsCount++;
418                console.log(`Новые чаты не появились (${noNewChatsCount}/${maxNoNewChatsAttempts})`);
419                
420                if (noNewChatsCount >= maxNoNewChatsAttempts) {
421                    console.log('Достигнут конец списка чатов (новые чаты не подгружаются)');
422                    break;
423                }
424            } else {
425                noNewChatsCount = 0; // Сбрасываем счетчик, если появились новые чаты
426            }
427            
428            previousChatCount = currentChats.length;
429            
430            // Скроллим для подгрузки следующей порции
431            const scrollSuccess = await scrollChatList();
432            if (!scrollSuccess) {
433                console.error('Ошибка скролла, останавливаем сбор');
434                break;
435            }
436            
437            scrollAttempts++;
438            
439            // Сохраняем базу каждые 50 контактов
440            if ((newContacts + updatedContacts) % 50 === 0 && (newContacts + updatedContacts) > 0) {
441                await saveContactsDatabase(db);
442                console.log('Промежуточное сохранение базы данных');
443            }
444        }
445        
446        // Финальное сохранение
447        await saveContactsDatabase(db);
448        
449        const finalCount = Object.keys(db.contacts).length;
450        console.log('=== СБОР БАЗЫ ЗАВЕРШЕН ===');
451        console.log('Всего контактов в базе:', finalCount);
452        console.log('Было:', initialCount, '| Стало:', finalCount, '| Добавлено:', newContacts, '| Обновлено:', updatedContacts);
453        
454        const statusMessage = testMode 
455            ? `Тестовый сбор завершен! Собрано: ${newContacts + updatedContacts} контакт`
456            : `Всего в базе: ${finalCount} контактов | Добавлено: ${newContacts} | Обновлено: ${updatedContacts}`;
457        
458        updateStatus('Сбор базы завершен!', statusMessage);
459        
460        // Сбрасываем флаги
461        shouldStop = false;
462        
463        return {
464            total: finalCount,
465            added: newContacts,
466            updated: updatedContacts
467        };
468    }
469
470    // Функция для создания модального окна
471    function createModal() {
472        const modalHTML = `
473            <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;">
474                <div style="background: white; padding: 30px; border-radius: 12px; width: 600px; max-width: 90%; max-height: 90vh; overflow-y: auto; box-shadow: 0 4px 20px rgba(0,0,0,0.3);">
475                    <h2 style="margin: 0 0 20px 0; color: #333; font-size: 24px;">Массовая рассылка</h2>
476                    
477                    <!-- Блок управления базой данных -->
478                    <div style="margin-bottom: 25px; padding: 20px; background: #f9f9f9; border-radius: 8px; border: 1px solid #e0e0e0;">
479                        <h3 style="margin: 0 0 15px 0; color: #555; font-size: 18px;">📊 База контактов</h3>
480                        <div id="dbStatsBlock" style="margin-bottom: 15px; padding: 12px; background: white; border-radius: 6px; font-size: 13px; color: #666;">
481                            <div>Загрузка статистики...</div>
482                        </div>
483                        <div style="margin-bottom: 12px;">
484                            <label style="display: flex; align-items: center; cursor: pointer;">
485                                <input type="checkbox" id="testModeCollect" checked style="width: 16px; height: 16px; margin-right: 8px; cursor: pointer;">
486                                <span style="color: #555; font-weight: 500; font-size: 13px;">Тестовый режим сбора (только 1 контакт)</span>
487                            </label>
488                        </div>
489                        <div style="display: flex; gap: 8px; flex-wrap: wrap;">
490                            <button id="collectDbButton" style="padding: 10px 18px; background: #4caf50; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">🔄 Собрать базу</button>
491                            <button id="updateDbButton" style="padding: 10px 18px; background: #2196f3; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">♻️ Обновить базу</button>
492                            <button id="viewDbButton" style="padding: 10px 18px; background: #9c27b0; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">👁️ Просмотр базы</button>
493                            <button id="pauseDbButton" style="display: none; padding: 10px 18px; background: #ff9800; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">⏸️ Пауза</button>
494                            <button id="stopDbButton" style="display: none; padding: 10px 18px; background: #f44336; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">⏹️ Стоп</button>
495                        </div>
496                    </div>
497                    
498                    <div style="margin-bottom: 20px;">
499                        <label style="display: block; margin-bottom: 8px; color: #555; font-weight: 600;">Текст сообщения:</label>
500                        <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>
501                    </div>
502                    
503                    <div style="margin-bottom: 25px;">
504                        <label style="display: block; margin-bottom: 8px; color: #555; font-weight: 600;">Дата последнего сообщения (до какой даты отправляли):</label>
505                        <input type="date" id="filterDate" style="width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;">
506                        <small style="color: #777; display: block; margin-top: 5px;">Будут обработаны чаты с датой последнего сообщения до указанной включительно</small>
507                    </div>
508                    
509                    <div style="margin-bottom: 20px;">
510                        <label style="display: flex; align-items: center; cursor: pointer;">
511                            <input type="checkbox" id="testMode" checked style="width: 18px; height: 18px; margin-right: 10px; cursor: pointer;">
512                            <span style="color: #555; font-weight: 600;">Тестовый режим (отправить только 1 сообщение)</span>
513                        </label>
514                        <small style="color: #ff6600; display: block; margin-top: 5px; margin-left: 28px;">⚠️ Рекомендуется для первого запуска</small>
515                    </div>
516                    
517                    <div id="statusBlock" style="display: none; margin-bottom: 20px; padding: 15px; background: #f0f8ff; border-radius: 6px; border-left: 4px solid #0066cc;">
518                        <div style="font-weight: 600; color: #0066cc; margin-bottom: 5px;">Статус:</div>
519                        <div id="statusText" style="color: #333;">Готов к запуску</div>
520                        <div id="progressText" style="color: #666; margin-top: 5px; font-size: 13px;"></div>
521                    </div>
522                    
523                    <div style="display: flex; gap: 10px; justify-content: flex-end;">
524                        <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>
525                        <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>
526                        <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>
527                        <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>
528                    </div>
529                </div>
530            </div>
531        `;
532        
533        document.body.insertAdjacentHTML('beforeend', modalHTML);
534        console.log('Модальное окно создано');
535        
536        // Обработчики событий
537        document.getElementById('closeButton').addEventListener('click', closeModal);
538        document.getElementById('startButton').addEventListener('click', () => {
539            console.log('!!! КЛИК ПО КНОПКЕ ЗАПУСТИТЬ !!!');
540            startBulkSending();
541        });
542        document.getElementById('pauseButton').addEventListener('click', togglePause);
543        document.getElementById('stopButton').addEventListener('click', stopBulkSending);
544        
545        // Обработчики для кнопок управления базой
546        document.getElementById('collectDbButton').addEventListener('click', async () => {
547            const testModeCollect = document.getElementById('testModeCollect').checked;
548            const confirmMessage = testModeCollect 
549                ? 'Начать тестовый сбор базы контактов (только 1 контакт)?'
550                : 'Начать полный сбор базы контактов? Это может занять некоторое время.';
551            
552            if (confirm(confirmMessage)) {
553                // Показываем кнопки паузы и остановки
554                document.getElementById('collectDbButton').style.display = 'none';
555                document.getElementById('updateDbButton').style.display = 'none';
556                document.getElementById('pauseDbButton').style.display = 'inline-block';
557                document.getElementById('stopDbButton').style.display = 'inline-block';
558                
559                isRunning = true;
560                shouldStop = false;
561                isPaused = false;
562                await collectContactsDatabase(false, testModeCollect);
563                isRunning = false;
564                
565                // Скрываем кнопки паузы и остановки
566                document.getElementById('collectDbButton').style.display = 'inline-block';
567                document.getElementById('updateDbButton').style.display = 'inline-block';
568                document.getElementById('pauseDbButton').style.display = 'none';
569                document.getElementById('stopDbButton').style.display = 'none';
570                
571                await updateDatabaseStats();
572            }
573        });
574        
575        document.getElementById('updateDbButton').addEventListener('click', async () => {
576            const testModeCollect = document.getElementById('testModeCollect').checked;
577            const confirmMessage = testModeCollect 
578                ? 'Обновить базу контактов в тестовом режиме (только 1 контакт)?'
579                : 'Обновить существующую базу контактов?';
580            
581            if (confirm(confirmMessage)) {
582                // Показываем кнопки паузы и остановки
583                document.getElementById('collectDbButton').style.display = 'none';
584                document.getElementById('updateDbButton').style.display = 'none';
585                document.getElementById('pauseDbButton').style.display = 'inline-block';
586                document.getElementById('stopDbButton').style.display = 'inline-block';
587                
588                isRunning = true;
589                shouldStop = false;
590                isPaused = false;
591                await collectContactsDatabase(true, testModeCollect);
592                isRunning = false;
593                
594                // Скрываем кнопки паузы и остановки
595                document.getElementById('collectDbButton').style.display = 'inline-block';
596                document.getElementById('updateDbButton').style.display = 'inline-block';
597                document.getElementById('pauseDbButton').style.display = 'none';
598                document.getElementById('stopDbButton').style.display = 'none';
599                
600                await updateDatabaseStats();
601            }
602        });
603        
604        // Обработчик паузы для сбора базы
605        document.getElementById('pauseDbButton').addEventListener('click', () => {
606            isPaused = !isPaused;
607            const pauseDbButton = document.getElementById('pauseDbButton');
608            
609            if (isPaused) {
610                pauseDbButton.textContent = '▶️ Продолжить';
611                pauseDbButton.style.background = '#4caf50';
612                console.log('Сбор базы приостановлен');
613            } else {
614                pauseDbButton.textContent = '⏸️ Пауза';
615                pauseDbButton.style.background = '#ff9800';
616                console.log('Сбор базы возобновлен');
617            }
618        });
619        
620        // Обработчик остановки для сбора базы
621        document.getElementById('stopDbButton').addEventListener('click', () => {
622            if (confirm('Вы уверены, что хотите остановить сбор базы?')) {
623                shouldStop = true;
624                isPaused = false;
625                console.log('Запрошена остановка сбора базы');
626            }
627        });
628        
629        // Обработчик для кнопки просмотра базы
630        document.getElementById('viewDbButton').addEventListener('click', openViewDatabaseModal);
631        
632        // Загружаем статистику при создании модального окна
633        updateDatabaseStats();
634    }
635    
636    // Функция для обновления статистики базы данных в интерфейсе
637    async function updateDatabaseStats() {
638        const db = await loadContactsDatabase();
639        const stats = getDatabaseStats(db);
640        const statsBlock = document.getElementById('dbStatsBlock');
641        
642        if (statsBlock) {
643            const lastUpdateText = stats.lastUpdate 
644                ? new Date(stats.lastUpdate).toLocaleString('ru-RU')
645                : 'Никогда';
646            
647            statsBlock.innerHTML = `
648                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
649                    <div><strong>Всего контактов:</strong> ${stats.total}</div>
650                    <div><strong>С датами:</strong> ${stats.withDates}</div>
651                    <div><strong>Отправлено:</strong> ${stats.sent}</div>
652                    <div><strong>Не отправлено:</strong> ${stats.notSent}</div>
653                </div>
654                <div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #e0e0e0; font-size: 12px; color: #999;">
655                    Последнее обновление: ${lastUpdateText}
656                </div>
657            `;
658        }
659    }
660
661    // Функция для открытия модального окна
662    function openModal() {
663        const modal = document.getElementById('bulkSenderModal');
664        if (modal) {
665            modal.style.display = 'flex';
666            console.log('Модальное окно открыто');
667        }
668    }
669
670    // Функция для закрытия модального окна
671    function closeModal() {
672        const modal = document.getElementById('bulkSenderModal');
673        if (modal && !isRunning) {
674            modal.style.display = 'none';
675            console.log('Модальное окно закрыто');
676        } else if (isRunning) {
677            alert('Дождитесь завершения рассылки или нажмите "Стоп"');
678        }
679    }
680
681    // Функция для обновления статуса
682    function updateStatus(status, progress = '') {
683        const statusBlock = document.getElementById('statusBlock');
684        const statusText = document.getElementById('statusText');
685        const progressText = document.getElementById('progressText');
686        
687        if (statusBlock && statusText) {
688            statusBlock.style.display = 'block';
689            statusText.textContent = status;
690            progressText.textContent = progress;
691            console.log('Статус:', status, progress);
692        }
693    }
694
695    // Функция для парсинга даты из текста
696    function parseDate(dateText) {
697        console.log('Парсинг даты:', dateText);
698        
699        if (!dateText) {
700            console.log('Пустой текст даты');
701            return null;
702        }
703        
704        const trimmed = dateText.trim();
705        
706        // Если формат "10:29" (время) - берем текущую дату
707        if (trimmed.includes(':')) {
708            const today = new Date();
709            console.log('Распознано время, используем текущую дату:', today);
710            return today;
711        }
712        
713        // Если формат "23.01" (день.месяц) или "31.12.2025" (день.месяц.год)
714        if (trimmed.includes('.')) {
715            const parts = trimmed.split('.');
716            if (parts.length === 2) {
717                // Формат "23.01"
718                const day = parseInt(parts[0]);
719                const month = parseInt(parts[1]) - 1; // Месяцы с 0
720                const currentYear = new Date().getFullYear();
721                
722                if (!isNaN(day) && !isNaN(month)) {
723                    const date = new Date(currentYear, month, day);
724                    console.log('Распознанная дата (день.месяц):', date);
725                    return date;
726                }
727            } else if (parts.length === 3) {
728                // Формат "31.12.2025"
729                const day = parseInt(parts[0]);
730                const month = parseInt(parts[1]) - 1; // Месяцы с 0
731                const year = parseInt(parts[2]);
732                
733                if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
734                    const date = new Date(year, month, day);
735                    console.log('Распознанная дата (день.месяц.год):', date);
736                    return date;
737                }
738            }
739        }
740        
741        // Если дата в формате "24 января" или "21 июня 2025"
742        const months = {
743            'января': 0, 'февраля': 1, 'марта': 2, 'апреля': 3,
744            'мая': 4, 'июня': 5, 'июля': 6, 'августа': 7,
745            'сентября': 8, 'октября': 9, 'ноября': 10, 'декабря': 11
746        };
747        
748        const parts = trimmed.split(' ');
749        
750        // Формат "21 июня 2025"
751        if (parts.length === 3) {
752            const day = parseInt(parts[0]);
753            const month = months[parts[1].toLowerCase()];
754            const year = parseInt(parts[2]);
755            
756            if (!isNaN(day) && month !== undefined && !isNaN(year)) {
757                const date = new Date(year, month, day);
758                console.log('Распознанная дата (день месяц год):', date);
759                return date;
760            }
761        }
762        
763        // Формат "24 января"
764        if (parts.length === 2) {
765            const day = parseInt(parts[0]);
766            const month = months[parts[1].toLowerCase()];
767            
768            if (!isNaN(day) && month !== undefined) {
769                const currentYear = new Date().getFullYear();
770                const date = new Date(currentYear, month, day);
771                console.log('Распознанная дата (день месяц):', date);
772                return date;
773            }
774        }
775        
776        console.log('Не удалось распознать дату');
777        return null;
778    }
779
780    // Функция для получения всех чатов
781    function getAllChats() {
782        // Сначала проверяем, есть ли результаты поиска (класс index_chat_EHlBq)
783        const searchChats = document.querySelectorAll('.index_chat_EHlBq');
784        
785        if (searchChats.length > 0) {
786            // Если есть результаты поиска, используем их
787            console.log('Найдены результаты поиска:', searchChats.length);
788            return Array.from(searchChats);
789        }
790        
791        // Если поиска нет, используем обычные чаты
792        const chats = document.querySelectorAll('.index_chat_4fr82');
793        // Фильтруем только видимые чаты (учитываем фильтр поиска)
794        const visibleChats = Array.from(chats).filter(chat => {
795            const style = window.getComputedStyle(chat);
796            // Проверяем, что чат не скрыт (убрали проверку высоты, т.к. чаты виртуализированы)
797            return style.display !== 'none' && 
798                   style.visibility !== 'hidden' && 
799                   style.opacity !== '0' &&
800                   chat.offsetParent !== null; // Проверяем offsetParent для фильтрации
801        });
802        console.log('Найдено чатов:', visibleChats.length, '(всего в DOM:', chats.length, ')');
803        return visibleChats;
804    }
805
806    // Функция для клика по чату
807    async function clickChat(chat) {
808        // Пробуем оба селектора для имени чата (обычный и поисковый)
809        const chatName = chat.querySelector('.index_chatTitle_TiXTq')?.textContent || 
810                        chat.querySelector('.index_chatTitle_x9txX')?.textContent || 
811                        'Неизвестно';
812        console.log('Клик по чату:', chatName);
813        chat.click();
814        await sleep(2000); // Ждем загрузки чата
815    }
816    
817    // Функция для открытия чата по ID (напрямую через URL)
818    async function openChatById(chatId) {
819        const currentUrl = new URL(window.location.href);
820        currentUrl.searchParams.set('id', chatId);
821        
822        console.log('Открытие чата по ID:', chatId);
823        console.log('URL:', currentUrl.toString());
824        
825        // Меняем URL без перезагрузки страницы
826        window.history.pushState({}, '', currentUrl.toString());
827        
828        // Триггерим событие для обновления интерфейса
829        window.dispatchEvent(new PopStateEvent('popstate'));
830        
831        // Ждем загрузки чата
832        await sleep(3000);
833        
834        return true;
835    }
836
837    // Функция для получения даты последнего сообщения в открытом чате
838    function getLastMessageDate() {
839        const dateElements = document.querySelectorAll('.om_1_n4');
840        if (dateElements.length > 0) {
841            // Берем последнюю дату (последнее сообщение)
842            const lastDateElement = dateElements[dateElements.length - 1];
843            const dateText = lastDateElement.textContent.trim();
844            console.log('Дата последнего сообщения:', dateText);
845            return parseDate(dateText);
846        }
847        console.log('Элемент даты не найден');
848        return null;
849    }
850
851    // Функция для отправки сообщения
852    async function sendMessage(messageText) {
853        console.log('Отправка сообщения:', messageText);
854        
855        // Находим текстовое поле
856        const textarea = document.querySelector('.om_17_a4');
857        if (!textarea) {
858            console.error('Текстовое поле не найдено');
859            return false;
860        }
861        
862        // Вставляем текст
863        textarea.value = messageText;
864        textarea.dispatchEvent(new Event('input', { bubbles: true }));
865        console.log('Текст вставлен в поле');
866        
867        // Ждем 1 секунду
868        await sleep(1000);
869        
870        // Находим кнопку отправки (вторая кнопка с классом om_17_a8)
871        const sendButtons = document.querySelectorAll('.om_17_a8');
872        if (sendButtons.length < 2) {
873            console.error('Кнопка отправки не найдена');
874            return false;
875        }
876        
877        // Кликаем на вторую кнопку (первая - это прикрепление файла)
878        const sendButton = sendButtons[1];
879        sendButton.click();
880        console.log('Сообщение отправлено (клик выполнен)');
881        
882        // Ждем 1 секунду после отправки
883        await sleep(1000);
884        
885        return true;
886    }
887
888    // Функция задержки
889    function sleep(ms) {
890        return new Promise(resolve => setTimeout(resolve, ms));
891    }
892
893    // Основная функция рассылки
894    async function startBulkSending() {
895        console.log('=== ФУНКЦИЯ startBulkSending ВЫЗВАНА ===');
896        
897        const messageText = document.getElementById('messageText').value.trim();
898        const filterDateInput = document.getElementById('filterDate').value;
899        const testModeCheckbox = document.getElementById('testMode');
900        const isTestMode = testModeCheckbox ? testModeCheckbox.checked : false;
901        
902        console.log('Текст сообщения:', messageText);
903        console.log('Дата фильтра:', filterDateInput);
904        console.log('Тестовый режим:', isTestMode);
905        
906        if (!messageText) {
907            alert('Введите текст сообщения');
908            console.log('Остановка: нет текста сообщения');
909            return;
910        }
911        
912        if (!filterDateInput) {
913            alert('Выберите дату фильтра');
914            console.log('Остановка: нет даты фильтра');
915            return;
916        }
917        
918        // Загружаем базу данных
919        const db = await loadContactsDatabase();
920        const totalContacts = Object.keys(db.contacts).length;
921        
922        if (totalContacts === 0) {
923            alert('База контактов пуста! Сначала соберите базу контактов с помощью кнопки "Собрать базу".');
924            console.log('Остановка: база контактов пуста');
925            return;
926        }
927        
928        const filterDate = new Date(filterDateInput);
929        filterDate.setHours(23, 59, 59, 999); // Устанавливаем конец дня для корректного сравнения
930        
931        console.log('Начало рассылки по базе данных');
932        console.log('Всего контактов в базе:', totalContacts);
933        console.log('Фильтр по дате:', filterDate);
934        console.log('Тестовый режим:', isTestMode ? 'ДА (только 1 сообщение)' : 'НЕТ');
935        
936        // Фильтруем контакты по дате
937        const contactsToSend = Object.values(db.contacts).filter(contact => {
938            if (!contact.lastMessageDate) return false;
939            const contactDate = new Date(contact.lastMessageDate);
940            return contactDate <= filterDate;
941        });
942        
943        console.log('Контактов подходящих по дате:', contactsToSend.length);
944        
945        if (contactsToSend.length === 0) {
946            alert('Нет контактов, подходящих по указанной дате.');
947            return;
948        }
949        
950        // Показываем статистику фильтрации
951        const filterStats = `Всего в базе: ${totalContacts} | Подходящих по фильтру: ${contactsToSend.length}`;
952        console.log(filterStats);
953        
954        // Меняем интерфейс
955        isRunning = true;
956        isPaused = false;
957        shouldStop = false;
958        
959        document.getElementById('startButton').style.display = 'none';
960        document.getElementById('pauseButton').style.display = 'inline-block';
961        document.getElementById('stopButton').style.display = 'inline-block';
962        document.getElementById('messageText').disabled = true;
963        document.getElementById('filterDate').disabled = true;
964        if (testModeCheckbox) testModeCheckbox.disabled = true;
965        
966        updateStatus(
967            'Запуск рассылки по базе данных...',
968            isTestMode ? `Тестовый режим: будет отправлено только 1 сообщение | ${filterStats}` : `${filterStats}`
969        );
970        
971        let processed = 0;
972        let sent = 0;
973        let skipped = 0;
974        
975        for (let i = 0; i < contactsToSend.length; i++) {
976            // Проверка на паузу
977            while (isPaused && !shouldStop) {
978                updateStatus('Пауза', `Обработано: ${processed}/${contactsToSend.length}, Отправлено: ${sent}, Пропущено: ${skipped}`);
979                await sleep(500);
980            }
981            
982            // Проверка на остановку
983            if (shouldStop) {
984                updateStatus('Остановлено пользователем', `Обработано: ${processed}/${contactsToSend.length}, Отправлено: ${sent}, Пропущено: ${skipped}`);
985                break;
986            }
987            
988            // Проверка тестового режима
989            if (isTestMode && sent >= 1) {
990                console.log('Тестовый режим: достигнут лимит в 1 сообщение, останавливаем рассылку');
991                updateStatus('Тестовая рассылка завершена!', `Отправлено: ${sent} сообщение (тестовый режим)`);
992                break;
993            }
994            
995            const contact = contactsToSend[i];
996            
997            updateStatus(
998                `Обработка контакта: ${contact.name}`,
999                `Прогресс: ${i + 1}/${contactsToSend.length} | Отправлено: ${sent} | Пропущено: ${skipped}`
1000            );
1001            
1002            console.log(`[${i + 1}/${contactsToSend.length}] Открываем чат: ${contact.name} (${contact.id})`);
1003            
1004            // Открываем чат по ID
1005            await openChatById(contact.id);
1006            
1007            // Проверяем, что чат открылся
1008            await sleep(1000);
1009            
1010            // Отправляем сообщение
1011            const success = await sendMessage(messageText);
1012            
1013            if (success) {
1014                sent++;
1015                // Обновляем дату отправки в базе
1016                db.contacts[contact.id].lastSentDate = new Date().toISOString();
1017                db.contacts[contact.id].messageCount = (db.contacts[contact.id].messageCount || 0) + 1;
1018                console.log(`✅ Сообщение отправлено: ${contact.name}`);
1019            } else {
1020                skipped++;
1021                console.error(`❌ Ошибка отправки: ${contact.name}`);
1022            }
1023            
1024            processed++;
1025            
1026            // Сохраняем базу каждые 10 отправленных сообщений
1027            if (sent % 10 === 0 && sent > 0) {
1028                await saveContactsDatabase(db);
1029                console.log('Промежуточное сохранение базы данных');
1030            }
1031            
1032            // Пауза между сообщениями
1033            await sleep(2000);
1034        }
1035        
1036        // Финальное сохранение базы
1037        await saveContactsDatabase(db);
1038        
1039        // Завершение
1040        isRunning = false;
1041        isPaused = false;
1042        
1043        document.getElementById('startButton').style.display = 'inline-block';
1044        document.getElementById('pauseButton').style.display = 'none';
1045        document.getElementById('stopButton').style.display = 'none';
1046        document.getElementById('messageText').disabled = false;
1047        document.getElementById('filterDate').disabled = false;
1048        if (testModeCheckbox) testModeCheckbox.disabled = false;
1049        
1050        updateStatus(
1051            'Рассылка завершена!',
1052            `Всего обработано: ${processed} | Отправлено: ${sent} | Пропущено: ${skipped}`
1053        );
1054        
1055        // Обновляем статистику в интерфейсе
1056        await updateDatabaseStats();
1057        
1058        console.log('=== РАССЫЛКА ЗАВЕРШЕНА ===');
1059        console.log('Обработано:', processed);
1060        console.log('Отправлено:', sent);
1061        console.log('Пропущено:', skipped);
1062    }
1063
1064    // Функция паузы/возобновления
1065    function togglePause() {
1066        isPaused = !isPaused;
1067        const pauseButton = document.getElementById('pauseButton');
1068        
1069        if (isPaused) {
1070            pauseButton.textContent = 'Продолжить';
1071            pauseButton.style.background = '#4caf50';
1072            console.log('Рассылка приостановлена');
1073        } else {
1074            pauseButton.textContent = 'Пауза';
1075            pauseButton.style.background = '#ff9800';
1076            console.log('Рассылка возобновлена');
1077        }
1078    }
1079
1080    // Функция остановки
1081    function stopBulkSending() {
1082        if (confirm('Вы уверены, что хотите остановить рассылку?')) {
1083            shouldStop = true;
1084            isPaused = false;
1085            console.log('Запрошена остановка рассылки');
1086        }
1087    }
1088
1089    // Функция для создания плавающей кнопки
1090    function createFloatingButton() {
1091        // Проверяем, не создана ли уже кнопка
1092        if (document.getElementById('bulkSenderFloatingBtn')) {
1093            return;
1094        }
1095        
1096        const button = document.createElement('button');
1097        button.id = 'bulkSenderFloatingBtn';
1098        button.textContent = '📧 Рассылка';
1099        button.style.cssText = `
1100            position: fixed;
1101            bottom: 20px;
1102            right: 20px;
1103            padding: 15px 25px;
1104            background: #0066cc;
1105            color: white;
1106            border: none;
1107            border-radius: 50px;
1108            cursor: pointer;
1109            font-size: 16px;
1110            font-weight: 600;
1111            box-shadow: 0 4px 12px rgba(0, 102, 204, 0.4);
1112            z-index: 9999;
1113            transition: all 0.3s;
1114        `;
1115        
1116        button.addEventListener('mouseenter', () => {
1117            button.style.background = '#0052a3';
1118            button.style.transform = 'scale(1.05)';
1119            button.style.boxShadow = '0 6px 16px rgba(0, 102, 204, 0.6)';
1120        });
1121        
1122        button.addEventListener('mouseleave', () => {
1123            button.style.background = '#0066cc';
1124            button.style.transform = 'scale(1)';
1125            button.style.boxShadow = '0 4px 12px rgba(0, 102, 204, 0.4)';
1126        });
1127        
1128        button.addEventListener('click', openModal);
1129        
1130        document.body.appendChild(button);
1131        console.log('Плавающая кнопка создана');
1132    }
1133
1134    // Функция для создания фильтра по дате в списке чатов
1135    function createDateFilter() {
1136        // Проверяем, не создан ли уже фильтр
1137        if (document.getElementById('dateFilterContainer')) {
1138            return;
1139        }
1140        
1141        // Ищем контейнер с поиском
1142        const searchContainer = document.querySelector('.om_1_f0')?.parentElement;
1143        if (!searchContainer) {
1144            console.log('Контейнер для фильтра не найден');
1145            return;
1146        }
1147        
1148        // Создаем контейнер для фильтра
1149        const filterContainer = document.createElement('div');
1150        filterContainer.id = 'dateFilterContainer';
1151        filterContainer.style.cssText = `
1152            padding: 15px;
1153            background: #f5f5f5;
1154            border-bottom: 1px solid #ddd;
1155            display: flex;
1156            gap: 10px;
1157            align-items: center;
1158            flex-wrap: wrap;
1159        `;
1160        
1161        filterContainer.innerHTML = `
1162            <label style="font-size: 13px; font-weight: 600; color: #555;">Фильтр по дате последнего сообщения:</label>
1163            <input type="date" id="dateFilterFrom" style="width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;">
1164            <span style="color: #555;"></span>
1165            <input type="date" id="dateFilterTo" style="width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px;">
1166            <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>
1167            <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>
1168        `;
1169        
1170        // Вставляем фильтр перед списком чатов
1171        const chatList = document.querySelector('.om_1_f0');
1172        if (chatList && chatList.parentElement) {
1173            chatList.parentElement.insertBefore(filterContainer, chatList);
1174            console.log('Фильтр по дате создан');
1175            
1176            // Обработчики событий
1177            document.getElementById('applyDateFilter').addEventListener('click', applyDateFilter);
1178            document.getElementById('clearDateFilter').addEventListener('click', clearDateFilter);
1179        }
1180    }
1181
1182    // Функция применения фильтра по дате
1183    async function applyDateFilter() {
1184        const dateFrom = document.getElementById('dateFilterFrom').value;
1185        const dateTo = document.getElementById('dateFilterTo').value;
1186        
1187        if (!dateFrom || !dateTo) {
1188            alert('Выберите обе даты для фильтрации');
1189            return;
1190        }
1191        
1192        const filterFrom = new Date(dateFrom);
1193        const filterTo = new Date(dateTo);
1194        
1195        console.log('Применение фильтра по дате:', filterFrom, '-', filterTo);
1196        
1197        // Показываем индикатор загрузки
1198        const applyButton = document.getElementById('applyDateFilter');
1199        const originalText = applyButton.textContent;
1200        applyButton.textContent = 'Фильтрация...';
1201        applyButton.disabled = true;
1202        
1203        // Получаем все чаты
1204        const allChats = document.querySelectorAll('.index_chat_4fr82');
1205        let visibleCount = 0;
1206        let hiddenCount = 0;
1207        
1208        allChats.forEach(chat => {
1209            // Ищем дату в самом элементе чата (не открывая его)
1210            const dateElement = chat.querySelector('.index_chatDate_z4mNc, .index_chatDate_WJ-\\+mb');
1211            
1212            if (dateElement) {
1213                const dateText = dateElement.textContent.trim();
1214                const chatDate = parseDate(dateText);
1215                
1216                if (chatDate) {
1217                    // Проверяем, попадает ли дата в диапазон
1218                    if (chatDate >= filterFrom && chatDate <= filterTo) {
1219                        // Дата подходит - оставляем чат видимым
1220                        chat.style.display = 'grid';
1221                        visibleCount++;
1222                        console.log('Чат подходит:', chat.querySelector('.index_chatTitle_TiXTq')?.textContent, chatDate);
1223                    } else {
1224                        // Дата не подходит - скрываем чат
1225                        chat.style.display = 'none';
1226                        hiddenCount++;
1227                    }
1228                } else {
1229                    // Не удалось распознать дату - оставляем видимым
1230                    chat.style.display = 'grid';
1231                    visibleCount++;
1232                }
1233            } else {
1234                // Нет даты - оставляем видимым
1235                chat.style.display = 'grid';
1236                visibleCount++;
1237            }
1238        });
1239        
1240        // Восстанавливаем кнопку
1241        applyButton.textContent = originalText;
1242        applyButton.disabled = false;
1243        
1244        // Показываем кнопку сброса
1245        document.getElementById('clearDateFilter').style.display = 'inline-block';
1246        
1247        console.log(`Фильтр применен. Показано: ${visibleCount}, Скрыто: ${hiddenCount}`);
1248        alert(`Фильтр применен!\nПоказано чатов: ${visibleCount}\nСкрыто чатов: ${hiddenCount}`);
1249    }
1250
1251    // Функция сброса фильтра
1252    function clearDateFilter() {
1253        // Показываем все чаты
1254        const allChats = document.querySelectorAll('.index_chat_4fr82');
1255        allChats.forEach(chat => {
1256            chat.style.display = 'grid';
1257        });
1258        
1259        // Очищаем поля
1260        document.getElementById('dateFilterFrom').value = '';
1261        document.getElementById('dateFilterTo').value = '';
1262        
1263        // Скрываем кнопку сброса
1264        document.getElementById('clearDateFilter').style.display = 'none';
1265        
1266        console.log('Фильтр сброшен');
1267    }
1268
1269    // Инициализация
1270    function init() {
1271        console.log('Инициализация расширения');
1272        
1273        // Проверяем, что мы на странице мессенджера с покупателями
1274        if (!window.location.href.includes('group=customers_v2')) {
1275            console.log('Не на странице чатов с покупателями, расширение не активно');
1276            return;
1277        }
1278        
1279        console.log('Страница подходит, создаем интерфейс');
1280        
1281        // Создаем плавающую кнопку сразу
1282        createFloatingButton();
1283        createModal();
1284        
1285        // Ждем загрузки списка чатов и создаем фильтр
1286        setTimeout(() => {
1287            createDateFilter();
1288        }, 2000);
1289        
1290        console.log('Интерфейс создан');
1291    }
1292
1293    // Запуск при загрузке страницы
1294    if (document.readyState === 'loading') {
1295        document.addEventListener('DOMContentLoaded', init);
1296    } else {
1297        init();
1298    }
1299})();