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})();