commit e48d05eac31b89e22b328b802c2de38ec93f3ddc Author: MatrixHasYou Date: Wed Apr 29 11:05:22 2026 +0400 🚀 Initial commit: Mutt Email-to-AI Pipeline diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ac458a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.pdf +*.log +.export_progress +.phase2_progress +exported_emails/ diff --git a/.muttrc b/.muttrc new file mode 100644 index 0000000..c99c55e --- /dev/null +++ b/.muttrc @@ -0,0 +1,74 @@ +# Mutt configuration for Gmail +# Простая совместимая версия + +# === Личные данные === +set realname = "MatrixHasYou" +set from = "ktybdsq@gmail.com" +set use_from = yes + +# === IMAP (получение почты) === +set imap_user = "ktybdsq@gmail.com" +set imap_pass = "hrur gguo srtm jpue" +set folder = "imaps://imap.gmail.com/" +set spoolfile = "+INBOX" +set mailbox = "+INBOX" + +# === SMTP (отправка почты) === +set smtp_url = "smtps://your.email@gmail.com@smtp.gmail.com:465" +set smtp_pass = "YOUR_APP_PASSWORD" + +# === Настройки ящика === +set mail_check = 60 +set timeout = 60 +set sort = threads +set sort_aux = last-date-received + +# === Кодировки === +set charset = utf-8 +set send_charset = "us-ascii:utf-8" + +# === Кэш === +set header_cache = "~/.cache/mutt/headers" +set message_cachedir = "~/.cache/mutt/bodies" +set certificate_file = "~/.cache/mutt/certificates" + +# === Просмотр === +set pager_index_lines = 15 +set menu_scroll = yes +set mark_old = no +set forward_decode = yes +set include = yes +set forward_format = "Fwd: %s" + +# === Цвета (базовые) === +color indicator white blue +color status white blue +color error red blue +color tilde blue +color attachment brightgreen black +color search brightyellow blue +color header cyan blue +color quoted cyan blue +color signature cyan blue +color tree red blue +color normal white blue +color message cyan blue +color deletion red blue +color flag cyan blue +color tag yellow blue + +# === Индекс === +color index red blue "~N" +color index green blue "~O" +color index cyan blue "~Z" +color index yellow blue "~F" + +# === Алиасы === +source ~/.mutt/aliases + +# === Макросы === +macro index,pager G "[Gmail]/All Mail" "Go to Gmail All Mail" +macro index,pager S "[Gmail]/Sent Mail" "Go to Sent" +macro index,pager D "[Gmail]/Drafts" "Go to Drafts" +macro index,pager T "[Gmail]/Trash" "Go to Trash" +macro index,pager A "[Gmail]/Spam" "Go to Spam" diff --git a/analyze_emails.py b/analyze_emails.py new file mode 100644 index 0000000..457da93 --- /dev/null +++ b/analyze_emails.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +""" +Анализ экспортированных писем. +Извлекает метаданные из PDF файлов и создаёт таблицу переписки. +""" + +import os +import re +import json +from pathlib import Path +from datetime import datetime +import fitz # PyMuPDF + +BASE_OUTPUT = Path("/home/matrixhasyou/mutt/exported_emails") +ALL_FOLDER = BASE_OUTPUT / "all" + +# Папки с ответами (органы которым писали) +REPLY_FOLDERS = { + "gosuslugi.ru": "Госуслуги", + "gov.ru": "Правительство РФ", + "mailop.ru": "Почта России (mailop)", + "mvd.ru": "МВД России", + "rospotrebnadzor.ru": "Роспотребнадзор", + "russianpost.ru": "Почта России", + "ulgkh.ru": "УЛГКХ (Ульяновск)", + "ulgss.ru": "УЛГСС (Ульяновск)", +} + +def parse_pdf_metadata(filepath): + """Извлечь текст и метаданные из PDF.""" + try: + doc = fitz.open(filepath) + text = "" + for page_num in range(min(3, len(doc))): # Первые 3 страницы + text += doc[page_num].get_text() + doc.close() + return text + except Exception as e: + return f"ERROR: {e}" + +def extract_email_info(filename, text): + """Извлечь информацию о письме из имени файла и текста.""" + # Парсим имя файла: YYYY-MM-DD_sender_subject.pdf + parts = Path(filename).stem.split('_', 2) + if len(parts) >= 3: + date_str = parts[0] + sender = parts[1] + subject = parts[2] + else: + date_str = "unknown" + sender = "unknown" + subject = Path(filename).stem + + # Анализируем текст на наличие признаков ответа + is_reply = False + reply_type = None # "formal" или "substantive" + + text_lower = text.lower() + + # Ключевые слова для определения типа ответа + formal_keywords = [ + 'ваше обращение', 'рассмотрено', 'сообщаем', 'уведомляем', + 'в соответствии с', 'на основании', 'руководствуясь', + 'отказано', 'не усматривается', 'не имеется оснований', + 'в пределах компетенции', 'направляем в ваш адрес', + 'информация предоставляется', 'согласно', 'в рамках', + 'уважаемый', 'благодарим за обращение', + ] + + substantive_keywords = [ + 'удовлетворено', 'приняты меры', 'выявлены нарушения', + 'обязать', 'устранить', 'проведена проверка', + 'направлено предписание', 'штраф', 'предписани', + 'срок устранения', 'контроль', 'исполнено', + 'возбуждено дело', 'материалы переданы', + 'установлено', 'подтверждено', 'выдано', + ] + + formal_count = sum(1 for kw in formal_keywords if kw in text_lower) + substantive_count = sum(1 for kw in substantive_keywords if kw in text_lower) + + # Определяем тип + if formal_count > 0 or substantive_count > 0: + is_reply = True + if substantive_count > formal_count: + reply_type = "По существу ✓" + else: + reply_type = "Формальная отписка ⚠" + + # Извлекаем номер входящего (если есть) + incoming_number = None + number_patterns = [ + r'[№N]\s*[\.:]?\s*([A-Za-zА-Яа-я0-9\-\/]+)', + r'исх\.\s*[\.:]?\s*([A-Za-zА-Яа-я0-9\-\/]+)', + r'вх\.\s*[\.:]?\s*([A-Za-zА-Яа-я0-9\-\/]+)', + r'регистрационн\w*\s*[\.:]?\s*([A-Za-zА-Яа-я0-9\-\/]+)', + ] + for pattern in number_patterns: + match = re.search(pattern, text[:2000], re.IGNORECASE) + if match: + incoming_number = match.group(1) + break + + return { + 'date': date_str, + 'sender': sender, + 'subject': subject[:60], + 'is_reply': is_reply, + 'reply_type': reply_type, + 'incoming_number': incoming_number, + 'formal_score': formal_count, + 'substantive_score': substantive_count, + } + +def analyze_all_emails(): + """Анализировать все письма и вернуть статистику.""" + results = [] + + # Сканируем папки с ответами + for folder_name, folder_desc in REPLY_FOLDERS.items(): + folder_path = BASE_OUTPUT / folder_name + if not folder_path.exists(): + continue + + pdf_files = sorted(folder_path.glob("*.pdf")) + + for pdf_file in pdf_files: + text = parse_pdf_metadata(pdf_file) + info = extract_email_info(pdf_file.name, text) + info['folder'] = folder_desc + info['filename'] = pdf_file.name + results.append(info) + + return results + +def format_table(results): + """Форматировать результаты в красивую таблицу для Telegram.""" + # Сортируем по дате + results.sort(key=lambda x: x['date']) + + # Заголовок + table = "📊 **ОТЧЁТ ПО ПЕРЕПИСКЕ С ОРГАНАМИ**\n" + table += f"📅 Дата формирования: {datetime.now().strftime('%d.%m.%Y')}\n" + table += "━" * 50 + "\n\n" + + # Группируем по папкам + by_folder = {} + for r in results: + folder = r['folder'] + if folder not in by_folder: + by_folder[folder] = [] + by_folder[folder].append(r) + + total_sent = len(results) + total_replies = sum(1 for r in results if r['is_reply']) + total_formal = sum(1 for r in results if r['reply_type'] and 'Формальная' in r['reply_type']) + total_substantive = sum(1 for r in results if r['reply_type'] and 'существу' in r['reply_type']) + + # Таблица по органам + table += "📮 **ПО ОРГАНАМ:**\n\n" + + for folder, items in by_folder.items(): + replies = sum(1 for i in items if i['is_reply']) + reply_pct = (replies / len(items) * 100) if items else 0 + + table += f"**{folder}**\n" + table += f"├─ Писем: {len(items)}\n" + table += f"├─ Ответов: {replies} ({reply_pct:.0f}%)\n" + + # Последние 3 письма с деталями + recent = items[-3:] if len(items) > 3 else items + for idx, item in enumerate(recent): + num = len(items) - len(recent) + idx + 1 + status = "✅ Ответ" if item['is_reply'] else "⏳ Нет ответа" + reply_detail = f" → {item['reply_type']}" if item['reply_type'] else "" + incoming = f" [Вх.№ {item['incoming_number']}]" if item['incoming_number'] else "" + + table += f"├─ #{num} | {item['date']} | {status}{reply_detail}{incoming}\n" + table += f"│ 📝 {item['subject'][:45]}...\n" + + if len(items) > 3: + table += f"└─ ... и ещё {len(items) - 3} писем\n" + + table += "\n" + + # ИТОГО + table += "━" * 50 + "\n" + table += "📈 **ИТОГО:**\n\n" + + reply_pct_total = (total_replies / total_sent * 100) if total_sent else 0 + formal_pct = (total_formal / total_replies * 100) if total_replies else 0 + substantive_pct = (total_substantive / total_replies * 100) if total_replies else 0 + + table += f"✉️ Всего отправлено: **{total_sent}**\n" + table += f"✅ Получено ответов: **{total_replies}** ({reply_pct_total:.0f}%)\n" + table += f"⏳ Без ответа: **{total_sent - total_replies}**\n\n" + + if total_replies > 0: + table += "📊 **КАЧЕСТВО ОТВЕТОВ:**\n\n" + table += f"⚠️ Формальные отписки: **{total_formal}** ({formal_pct:.0f}%)\n" + table += f"✅ Ответы по существу: **{total_substantive}** ({substantive_pct:.0f}%)\n\n" + + table += "🎯 **ВЫВОД:**\n\n" + if substantive_pct > 50: + table += "Большинство ответов — **по существу**. Органы работают! 💪\n" + elif formal_pct > 70: + table += "Преобладают **формальные отписки**. Требуется escalation. ⚠️\n" + else: + table += "Смешанная картина. Нужно работать с каждым ответом отдельно.\n" + + table += "\n" + "━" * 50 + "\n" + table += f"🕐 Сформировано: {datetime.now().strftime('%H:%M %d.%m.%Y')}\n" + + return table + +if __name__ == '__main__': + print("Анализирую письма...") + results = analyze_all_emails() + print(f"Проанализировано {len(results)} писем\n") + + table = format_table(results) + print(table) + + # Сохраняем в файл + output_file = Path(__file__).parent / "report_table.txt" + output_file.write_text(table, encoding='utf-8') + print(f"\nОтчёт сохранён в {output_file}") diff --git a/analyze_emails_detailed.py b/analyze_emails_detailed.py new file mode 100644 index 0000000..410cefd --- /dev/null +++ b/analyze_emails_detailed.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +""" +Детальный анализ экспортированных писем — полная таблица для Telegram. +""" + +import os +import re +from pathlib import Path +from datetime import datetime +import fitz + +BASE_OUTPUT = Path("/home/matrixhasyou/mutt/exported_emails") + +REPLY_FOLDERS = { + "gosuslugi.ru": "🟦 Госуслуги", + "gov.ru": "🟪 Правительство РФ", + "mailop.ru": "🟨 Почта России (mailop)", + "mvd.ru": "🟥 МВД России", + "rospotrebnadzor.ru": "🟧 Роспотребнадзор", + "russianpost.ru": "📮 Почта России", + "ulgkh.ru": "🏢 УЛГКХ", + "ulgss.ru": "🏛 УЛГСС", +} + +def parse_pdf_metadata(filepath): + """Извлечь текст из PDF (первые 5 страниц).""" + try: + doc = fitz.open(filepath) + text = "" + for page_num in range(min(5, len(doc))): + text += doc[page_num].get_text() + doc.close() + return text + except Exception as e: + return f"ERROR: {e}" + +def extract_incoming_number(text): + """Извлечь номер входящего/исходящего.""" + patterns = [ + r'[№N]\s*[\.:]?\s*([A-Za-zА-Яа-я0-9\-\/\.]+)', + r'исх\.?\s*[\.:№\s]*\s*([A-Za-zА-Яа-я0-9\-\/\.]+)', + r'вх\.?\s*[\.:№\s]*\s*([A-Za-zА-Яа-я0-9\-\/\.]+)', + r'регистрации\s*[№:]\s*([A-Za-zА-Яа-я0-9\-\/\.]+)', + r'([0-9]{2,4}[-\/][A-Za-zА-я0-9]+[-\/][0-9]+)', + ] + + for pattern in patterns: + matches = re.finditer(pattern, text[:3000], re.IGNORECASE) + for match in matches: + val = match.group(1).strip() + # Фильтруем слишком короткие или мусорные значения + if len(val) > 3 and val.lower() not in ['reply', 'pdf', 'notification', 'fo']: + return val + return None + +def extract_response_date(text): + """Извлечь дату ответа.""" + date_patterns = [ + r'от\s+(\d{2}[\.\/]\d{2}[\.\/]\d{4})', + r'(\d{2}[\.\/]\d{2}[\.\/]\d{4})', + r'«(\d+)»\s+(\w+)\s+(\d{4})', + ] + for pattern in date_patterns: + match = re.search(pattern, text[:2000]) + if match: + return match.group(0)[:15] + return None + +def classify_reply(text): + """Определить тип ответа.""" + text_lower = text.lower() + + formal_markers = [ + 'ваше обращение рассмотрено', 'сообщаем вам', 'уведомляем', + 'в соответствии с федеральным законом', 'в пределах компетенции', + 'направляем в ваш адрес', 'в рамках действующего законодательства', + 'уважаемый', 'благодарим за обращение', 'ваше предложение', + 'разъясняем', 'доводим до вашего сведения', + 'не усматривается оснований', 'не представляет возможным', + 'в соответствии с положением', 'регулирующим', + 'ваше предложение не подлежит', 'отказано', + ] + + substantive_markers = [ + 'удовлетворено', 'приняты меры', 'выявлены нарушения', + 'обязать устранить', 'проведена проверка', 'выдано предписание', + 'направлено предписание', 'привлечен к ответственности', + 'возбуждено дело', 'штраф наложен', 'материалы переданы', + 'установлено нарушение', 'признано незаконным', + 'подлежит устранению', 'в срок до', 'контроль исполнения', + 'исполнено в полном объеме', 'восстановлено', + 'перерасчет', 'возмещено', 'компенсация', + ] + + formal_count = sum(1 for kw in formal_markers if kw in text_lower) + substantive_count = sum(1 for kw in substantive_markers if kw in text_lower) + + if formal_count == 0 and substantive_count == 0: + # Проверяем есть ли вообще ответ + has_response_indicators = any(kw in text_lower for kw in [ + 'ответ', 'рассмотрение', 'сообщаем', 'уведомляем', + 'уважаемый', 'обращение', 'письмо ваше' + ]) + if has_response_indicators: + return "Формальная ⚠️", formal_count, substantive_count + return None, 0, 0 + + if substantive_count > formal_count: + return "По существу ✅", formal_count, substantive_count + return "Формальная ⚠️", formal_count, substantive_count + +def analyze_email(filepath): + """Полный анализ одного письма.""" + filename = filepath.name + + # Парсим имя файла + parts = Path(filename).stem.split('_', 2) + if len(parts) >= 3: + date_str = parts[0] + sender = parts[1] + subject = parts[2] + else: + date_str = "unknown" + sender = "unknown" + subject = Path(filename).stem + + # Анализируем текст + text = parse_pdf_metadata(filepath) + + incoming_num = extract_incoming_number(text) + response_date = extract_response_date(text) + reply_type, formal_score, substantive_score = classify_reply(text) + + is_sent = True # Это исходящее письмо + has_reply = reply_type is not None + + return { + 'date': date_str, + 'sender': sender, + 'subject': subject[:55], + 'incoming_number': incoming_num, + 'response_date': response_date, + 'reply_type': reply_type, + 'has_reply': has_reply, + 'is_sent': is_sent, + 'formal_score': formal_score, + 'substantive_score': substantive_score, + } + +def build_telegram_report(): + """Построить полный отчёт для Telegram.""" + all_emails = [] + + # Сканируем все папки + for folder_name, folder_emoji in REPLY_FOLDERS.items(): + folder_path = BASE_OUTPUT / folder_name + if not folder_path.exists(): + continue + + pdf_files = sorted(folder_path.glob("*.pdf")) + + for pdf_file in pdf_files: + info = analyze_email(pdf_file) + info['folder_name'] = folder_name + info['folder_display'] = folder_emoji + all_emails.append(info) + + # Сортируем по дате + all_emails.sort(key=lambda x: x['date']) + + # Считаем статистику + total = len(all_emails) + with_replies = sum(1 for e in all_emails if e['has_reply']) + without_replies = total - with_replies + formal = sum(1 for e in all_emails if e['reply_type'] and 'Формальная' in str(e['reply_type'])) + substantive = sum(1 for e in all_emails if e['reply_type'] and 'существу' in str(e['reply_type'])) + + # Строим отчёт + report = [] + + report.append("📊 **ПОЛНЫЙ ОТЧЁТ ПО ПЕРЕПИСКЕ С ОРГАНАМИ ВЛАСТИ**") + report.append(f"📅 Дата формирования: {datetime.now().strftime('%d.%m.%Y %H:%M')}") + report.append("━" * 48) + report.append("") + + # Группируем по органам + by_folder = {} + for email in all_emails: + folder = email['folder_display'] + if folder not in by_folder: + by_folder[folder] = [] + by_folder[folder].append(email) + + # Для каждого органа + for folder_display, emails in by_folder.items(): + report.append(f"{'─' * 48}") + report.append(f"**{folder_display}**") + report.append(f"{'─' * 48}") + report.append("") + + replies_count = sum(1 for e in emails if e['has_reply']) + + # Таблица писем + report.append(f"{'№':<3} │ {'Дата':^10} │ {'Статус':^13} │ {'Вх. №':^18} │ Тема") + report.append(f"{'─' * 3}─┼─{'─' * 10}─┼─{'─' * 13}─┼─{'─' * 18}─┼─{'─' * 30}") + + for idx, email in enumerate(emails, 1): + status = "" + if email['has_reply']: + status = email['reply_type'][:13] + else: + status = "⏳ Нет ответа" + + incoming = email['incoming_number'] or "—" + if len(incoming) > 18: + incoming = incoming[:15] + "..." + + subject = email['subject'] + if len(subject) > 35: + subject = subject[:32] + "..." + + report.append(f"{idx:<3} │ {email['date']:^10} │ {status:^13} │ {incoming:^18} │ {subject}") + + report.append("") + report.append(f"📊 Итого по органу: {len(emails)} писем | Ответов: {replies_count} ({replies_count/len(emails)*100:.0f}%)") + report.append("") + + # ИТОГО + report.append("=" * 48) + report.append("📈 **СВОДНАЯ СТАТИСТИКА**") + report.append("=" * 48) + report.append("") + report.append(f"✉️ **Всего отправлено писем:** {total}") + report.append(f"✅ **Получено ответов:** {with_replies} ({with_replies/total*100:.0f}%)") + report.append(f"⏳ **Без ответа:** {without_replies} ({without_replies/total*100:.0f}%)") + report.append("") + + if with_replies > 0: + report.append("📊 **КАЧЕСТВО ОТВЕТОВ:**") + report.append(f" ⚠️ Формальные отписки: {formal} ({formal/with_replies*100:.0f}%)") + report.append(f" ✅ Ответы по существу: {substantive} ({substantive/with_replies*100:.0f}%)") + report.append("") + + report.append("🎯 **ВЫВОД:**") + if substantive / with_replies > 0.5: + report.append(" Большинство ответов — по существу. Органы работают! 💪") + elif formal / with_replies > 0.7: + report.append(" ⚠️ Преобладают формальные отписки. Требуется:") + report.append(" • Повторные обращения с требованием ответа по существу") + report.append(" • Жалобы в вышестоящие органы") + report.append(" • Обращение в прокуратуру на бездействие") + else: + report.append(" Смешанная картина. Требуется работа с каждым ответом.") + + report.append("") + report.append("=" * 48) + report.append(f"🕐 Отчёт сформирован: {datetime.now().strftime('%H:%M %d.%m.%Y')}") + report.append("=" * 48) + + return "\n".join(report) + +if __name__ == '__main__': + print("Формирую детальный отчёт...") + report = build_telegram_report() + print(report) + + # Сохраняем + output_file = Path(__file__).parent / "report_detailed.txt" + output_file.write_text(report, encoding='utf-8') + print(f"\n\n📄 Отчёт сохранён: {output_file}") diff --git a/consolidate_to_mega_pdf.py b/consolidate_to_mega_pdf.py new file mode 100755 index 0000000..2fa5ad6 --- /dev/null +++ b/consolidate_to_mega_pdf.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +""" +Consolidates all individual email PDFs into one giant PDF for NotebookLM. +Sorted by date (prefix in filename). +""" +import os +import sys +import fitz # PyMuPDF +from pathlib import Path +import logging +import requests +from datetime import datetime + +# === CONFIG === +BASE_DIR = Path(__file__).parent +SOURCE_DIR = BASE_DIR / "exported_emails" / "all" +OUTPUT_FILE = BASE_DIR / "ALL_EMAILS_CONSOLIDATED.pdf" +BOT_TOKEN = "8725618164:AAH1tGalq-pw1l0t4P5c0sdLkCVJdh7IE7M" +CHAT_ID = "197957361" +PROXY = "socks5h://127.0.0.1:10808" + +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") +log = logging.getLogger("Consolidator") + +def send_tg_alert(count, size_mb): + url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage" + text = f""" +🔥 ВАЖНО: СВОДНЫЙ ФАЙЛ ПОЧТЫ ОБНОВЛЕН! 🔥 + +📑 Файл: ALL_EMAILS_CONSOLIDATED.pdf +📧 Всего писем в архиве: {count} +🐘 Размер файла: {size_mb:.2f} МБ +📅 Обновлено: {datetime.now().strftime('%Y-%m-%d %H:%M')} + +🚀 СРОЧНО: Загрузи этот файл в Google Drive и обнови источники в NotebookLM! +Старые данные AI должен "забыть", новые — осознать. +""" + try: + requests.post(url, json={"chat_id": CHAT_ID, "text": text, "parse_mode": "HTML"}, + proxies={"https": PROXY}, timeout=20) + log.info("✅ Telegram alert sent.") + except Exception as e: + log.error(f"❌ TG Alert failed: {e}") + +def consolidate(): + log.info("Starting consolidation...") + + if not SOURCE_DIR.exists(): + log.error(f"Source directory {SOURCE_DIR} not found!") + return + + pdf_files = list(SOURCE_DIR.glob("*.pdf")) + if not pdf_files: + log.warning("No PDF files found to consolidate.") + return + + # Sort by filename (which starts with date YYYY-MM-DD or contains it) + # Most files are: domain_YYYY-MM-DD_... or YYYY-MM-DD_... + # We'll just use natural sort of filenames. + pdf_files.sort(key=lambda x: x.name) + + log.info(f"Found {len(pdf_files)} PDFs. Merging...") + + merger = fitz.open() + for pdf_path in pdf_files: + try: + doc = fitz.open(pdf_path) + merger.insert_pdf(doc) + doc.close() + except Exception as e: + log.warning(f"Failed to add {pdf_path.name}: {e}") + + if len(merger) > 0: + merger.save(OUTPUT_FILE, garbage=3, deflate=True) + size_mb = OUTPUT_FILE.stat().st_size / (1024 * 1024) + log.info(f"✅ Consolidated PDF saved: {OUTPUT_FILE} ({size_mb:.2f} MB)") + merger.close() + + # Send alert + send_tg_alert(len(pdf_files), size_mb) + else: + log.error("Merger result is empty.") + merger.close() + +if __name__ == "__main__": + consolidate() diff --git a/export_emails.py b/export_emails.py new file mode 100644 index 0000000..962d402 --- /dev/null +++ b/export_emails.py @@ -0,0 +1,577 @@ +#!/usr/bin/env python3 +""" +Gmail IMAP email exporter v3 — optimized. +Single IMAP query for all emails since Dec 20, 2025, then filter by domains in Python. +Each email becomes a PDF with body + attachments merged. +Organized by domain and also in a flat 'all' folder. +""" + +import os +import sys +import re +import time +import logging +from pathlib import Path +from datetime import datetime, date +from email.utils import parsedate_to_datetime + +from imap_tools import MailBox, AND +from weasyprint import HTML +from fpdf import FPDF +import fitz # PyMuPDF + +# === CONFIG === +IMAP_SERVER = "imap.gmail.com" + +# Извлекаем учётку из .muttrc (сначала в проекте, потом в home) +MUTT_CONF = Path(__file__).parent / ".muttrc" +if not MUTT_CONF.exists(): + MUTT_CONF = Path.home() / ".muttrc" + +# Domains to search +DOMAINS = [ + "gosuslugi.ru", "dom.gosuslugi.ru", "gov.ru", "duma.gov.ru", + "mvd.gov.ru", "mvd.ru", "rkn.gov.ru", "nalog.gov.ru", "nalog.ru", + "genproc.gov.ru", "pfr.gov.ru", "fss.ru", "rospotrebnadzor.ru", + "russianpost.ru", "mailop.ru", "73.mailop.ru", "uk-service.ru", + "ulgss.ru", "ulgov.ru", "ulgkh.ru", "dgi.ru", "mos.ru", "pfrf.ru", + "fsin.gov.ru", "skrf.ru", "epp.genproc.gov.ru", "ugpr.ru", + "fas.gov.ru", "minstroyrf.gov.ru", "gkh.ru", "sudrf.ru", +] + +# From this date +SINCE_DATE = date(2025, 12, 20) + +# Gmail folder to search +GMAIL_FOLDER = "[Gmail]/Вся почта" + +# Output +BASE_OUTPUT = Path(__file__).parent / "exported_emails" +ALL_FOLDER = BASE_OUTPUT / "all" + +# Progress tracking +PROGRESS_FILE = Path(__file__).parent / ".export_progress" + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler(Path(__file__).parent / "export.log", encoding="utf-8"), + logging.StreamHandler(sys.stdout), + ], +) +log = logging.getLogger(__name__) + + +def load_progress(): + """Load set of already processed UIDs.""" + if PROGRESS_FILE.exists(): + return set(PROGRESS_FILE.read_text().strip().splitlines()) + return set() + + +def save_progress(uid, processed_set): + """Save progress after each email.""" + processed_set.add(str(uid)) + PROGRESS_FILE.write_text("\n".join(processed_set)) + + +def sanitize_filename(name, max_len=80): + """Make filename safe.""" + name = re.sub(r'[<>:"/\\|?*\x00-\x1f]', '_', name) + name = re.sub(r'\s+', ' ', name).strip() + return name[:max_len] + + +def parse_mutt_config(): + """Extract IMAP user and password from .muttrc.""" + config = {} + with open(MUTT_CONF) as f: + for line in f: + line = line.strip() + if line.startswith('#') or not line: + continue + match = re.match(r'set\s+(\w+)\s*=\s*"([^"]*)"', line) + if not match: + match = re.match(r'set\s+(\w+)\s*=\s*(\S+)', line) + if match: + key, val = match.group(1), match.group(2) + config[key] = val + return config + + +def html_to_pdf_stream(html_content): + """Convert HTML to PDF bytes using weasyprint, fallback to fpdf.""" + try: + html_doc = HTML(string=html_content) + return html_doc.write_pdf() + except Exception as e: + log.warning(f"weasyprint failed, using fpdf fallback: {e}") + return html_to_pdf_fpdf(html_content) + + +def html_to_pdf_fpdf(html_content): + """Fallback: strip HTML and use FPDF.""" + from bs4 import BeautifulSoup + soup = BeautifulSoup(html_content, 'lxml') + text = soup.get_text(separator='\n') + + pdf = FPDF() + pdf.add_page() + pdf.add_font('DejaVu', '', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', uni=True) + pdf.add_font('DejaVu', 'B', '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', uni=True) + pdf.set_font('DejaVu', '', 10) + + lines = [line for line in text.split('\n') if line.strip()] + for line in lines: + pdf.multi_cell(0, 6, line) + pdf.ln(1) + + return pdf.output() + + +def convert_attachment_to_pdf(attachment_data, filename, content_type): + """Convert an attachment to PDF.""" + ext = Path(filename).suffix.lower() + + try: + if ext in ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp', '.svg'): + pdf = FPDF() + pdf.add_page() + img_w, img_h = get_image_dims(attachment_data) + page_w = pdf.w - 20 + scale = min(page_w / img_w, (pdf.h - 20) / img_h, 1.0) + w, h = img_w * scale, img_h * scale + import tempfile + with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmp: + tmp.write(attachment_data) + tmp_path = tmp.name + try: + pdf.image(tmp_path, x=10, y=10, w=w, h=h) + finally: + os.unlink(tmp_path) + return pdf.output() + + elif ext in ('.doc', '.docx'): + import tempfile, subprocess + with tempfile.TemporaryDirectory() as tmpdir: + src = Path(tmpdir) / filename + src.write_bytes(attachment_data) + subprocess.run( + ['libreoffice', '--headless', '--convert-to', 'pdf', + '--outdir', tmpdir, str(src)], + capture_output=True, timeout=120 + ) + pdf_path = src.with_suffix('.pdf') + if pdf_path.exists(): + return pdf_path.read_bytes() + + elif ext == '.pdf': + return attachment_data + + elif ext in ('.xls', '.xlsx'): + import tempfile, subprocess + with tempfile.TemporaryDirectory() as tmpdir: + src = Path(tmpdir) / filename + src.write_bytes(attachment_data) + subprocess.run( + ['libreoffice', '--headless', '--convert-to', 'pdf', + '--outdir', tmpdir, str(src)], + capture_output=True, timeout=120 + ) + pdf_path = src.with_suffix('.pdf') + if pdf_path.exists(): + return pdf_path.read_bytes() + + elif ext in ('.txt', '.csv', '.log', '.xml', '.html', '.htm'): + try: + text = attachment_data.decode('utf-8', errors='replace') + except: + text = attachment_data.decode('latin-1', errors='replace') + return html_to_pdf_fpdf(text) + + else: + info = f"Attachment: {filename}\nType: {content_type}\nSize: {len(attachment_data)} bytes\n\n[Binary content - not converted]" + return html_to_pdf_fpdf(info) + + except Exception as e: + log.warning(f"Failed to convert {filename}: {e}") + info = f"Attachment: {filename}\nType: {content_type}\nSize: {len(attachment_data)} bytes\n\nConversion failed: {e}" + return html_to_pdf_fpdf(info) + + +def get_image_dims(data): + """Get image dimensions.""" + import io + from PIL import Image + img = Image.open(io.BytesIO(data)) + w_px, h_px = img.size + w_mm = w_px * 25.4 / 96 + h_mm = h_px * 25.4 / 96 + return w_mm, h_mm + + +def merge_pdfs(pdf_bytes_list): + """Merge multiple PDFs into one.""" + if not pdf_bytes_list: + return None + if len(pdf_bytes_list) == 1: + return pdf_bytes_list[0] + + merger = fitz.open() + for pdf_bytes in pdf_bytes_list: + try: + doc = fitz.open(stream=pdf_bytes, filetype="pdf") + merger.insert_pdf(doc) + except Exception as e: + log.warning(f"Failed to merge a PDF section: {e}") + + output = merger.tobytes() + merger.close() + return output + + +def build_email_html(msg): + """Build nice HTML representation of email with headers.""" + subject = msg.subject or "(Без темы)" + from_addr = msg.from_ or "unknown" + to_addr = ", ".join(msg.to) if msg.to else "unknown" + cc_addr = ", ".join(msg.cc) if msg.cc else "" + date_str = msg.date_str or str(msg.date) if msg.date else "unknown" + + body_html = msg.html + if not body_html and msg.text: + text = msg.text + text_escaped = text.replace('&', '&').replace('<', '<').replace('>', '>') + body_html = text_escaped.replace('\n', '
\n') + elif not body_html: + body_html = "

(Нет тела письма)

" + + attachments_info = "" + if msg.attachments: + att_items = [] + for att in msg.attachments: + att_items.append(f'
  • {att.filename} ({att.content_type}, {att.size} bytes)
  • ') + attachments_info = '

    Вложения:

      ' + ''.join(att_items) + '
    ' + + html = f""" + + + + + + +
    +

    {subject}

    + + + + {f'' if cc_addr else ''} + +
    От:{from_addr}
    Кому:{to_addr}
    Копия:{cc_addr}
    Дата:{date_str}
    +
    +
    + {body_html} +
    + {f'
    {attachments_info}
    ' if attachments_info else ''} + + + """ + return html + + +def matches_domain(addr, domains): + """Check if address matches any domain. Returns matched domain or None.""" + if not addr: + return None + addr_lower = addr.lower() + for domain in domains: + if domain.lower() in addr_lower: + return domain + return None + + +def main(): + log.info("=" * 60) + log.info("Gmail Email Exporter v3 — starting") + log.info("=" * 60) + + # Parse config + config = parse_mutt_config() + imap_user = config.get('imap_user', '') + imap_pass = config.get('imap_pass', '') + + if not imap_user or imap_user == 'your.email@gmail.com': + log.error("ERROR: Не настроены учётные данные в .muttrc!") + log.error("Укажите реальный imap_user и imap_pass (App Password)") + sys.exit(1) + + log.info(f"IMAP User: {imap_user}") + log.info(f"Domains: {len(DOMAINS)} domains") + log.info(f"Since: {SINCE_DATE.strftime('%Y-%m-%d')}") + + # Create output dirs + BASE_OUTPUT.mkdir(parents=True, exist_ok=True) + ALL_FOLDER.mkdir(parents=True, exist_ok=True) + + # Load progress + processed = load_progress() + log.info(f"Previously processed: {len(processed)} emails") + + # Connect + log.info("Connecting to Gmail IMAP...") + try: + mailbox = MailBox(IMAP_SERVER).login(imap_user, imap_pass, initial_folder=GMAIL_FOLDER) + log.info("Connected successfully!") + except Exception as e: + log.error(f"Failed to connect: {e}") + sys.exit(1) + + # Phase 1: Fetch headers only to find matching emails + log.info("Phase 1: Scanning emails (headers only) to find matches...") + scan_start = time.time() + + matching_uids = [] + total_scanned = 0 + last_log_time = time.time() + + # Fetch headers only first — much faster + for msg in mailbox.fetch( + AND(date_gte=SINCE_DATE), + bulk=True, + headers_only=True, + mark_seen=False, + ): + total_scanned += 1 + + # Log every 10 seconds + now = time.time() + if now - last_log_time > 10: + elapsed = now - scan_start + rate = total_scanned / elapsed if elapsed > 0 else 0 + log.info(f" Scanned: {total_scanned} ({rate:.0f}/sec), Matching so far: {len(matching_uids)}") + last_log_time = now + + # Check if matches any domain + from_match = matches_domain(msg.from_, DOMAINS) + to_match = None + if not from_match: + for to_addr in (msg.to or []): + to_match = matches_domain(to_addr, DOMAINS) + if to_match: + break + + if from_match or to_match: + # Also verify date since IMAP date filtering can be imprecise + msg_date = msg.date + if msg_date and hasattr(msg_date, 'date'): + msg_date = msg_date.date() + elif isinstance(msg_date, datetime): + pass # ok + + if not msg_date or msg_date >= SINCE_DATE: + matching_uids.append(msg.uid) + + scan_elapsed = time.time() - scan_start + log.info(f"Phase 1 complete: scanned {total_scanned} emails in {scan_elapsed/60:.1f}min") + log.info(f"Found {len(matching_uids)} matching emails") + + if not matching_uids: + log.info("No matching emails found. Exiting.") + mailbox.logout() + return + + # Phase 2: Download full emails and convert to PDF + log.info("\n" + "=" * 60) + log.info("Phase 2: Downloading full emails and converting to PDF...") + log.info("=" * 60) + + start_time = time.time() + total_processed = 0 + total_errors = 0 + + def reconnect_mailbox(): + """Reconnect to IMAP with retry.""" + for attempt in range(5): + try: + log.info(f"Reconnecting... attempt {attempt+1}") + mb = MailBox(IMAP_SERVER).login(imap_user, imap_pass, initial_folder=GMAIL_FOLDER) + log.info("Reconnected!") + return mb + except Exception as e: + log.warning(f"Reconnect failed: {e}") + time.sleep(10 * (attempt + 1)) + log.error("Failed to reconnect after 5 attempts") + sys.exit(1) + + for idx, uid in enumerate(matching_uids, 1): + if str(uid) in processed: + log.info(f"[{idx}/{len(matching_uids)}] SKIP (already processed) UID={uid}") + continue + + elapsed = time.time() - start_time + if idx > 1: + eta = (elapsed / (idx - 1)) * (len(matching_uids) - idx + 1) + else: + eta = 0 + + log.info(f"[{idx}/{len(matching_uids)}] Downloading UID={uid} | ETA: {eta/60:.1f}m") + + try: + # Fetch full message + msgs = list(mailbox.fetch( + AND(uid=uid), + headers_only=False, + mark_seen=False, + )) + if not msgs: + log.warning(f" Could not fetch UID={uid}") + total_errors += 1 + save_progress(uid, processed) + continue + + msg = msgs[0] + + # Build email info + subject = msg.subject or "(Без темы)" + from_addr = msg.from_ or "unknown" + date_str = msg.date_str or "unknown" + + # Determine matching domain + match_domain = matches_domain(from_addr, DOMAINS) + if not match_domain: + for to_addr in (msg.to or []): + match_domain = matches_domain(to_addr, DOMAINS) + if match_domain: + break + if not match_domain: + match_domain = "other" + + safe_subject = sanitize_filename(subject) + safe_from = sanitize_filename(from_addr.split('<')[-1].rstrip('>').split('@')[0], 30) + date_part = msg.date.strftime('%Y-%m-%d') if msg.date else 'unknown' + + filename = f"{date_part}_{safe_from}_{safe_subject}.pdf" + if len(filename) > 150: + filename = filename[:150] + ".pdf" + + # Build HTML for email body + html_content = build_email_html(msg) + + pdf_parts = [] + + # Convert body to PDF + body_pdf = html_to_pdf_stream(html_content) + if body_pdf: + pdf_parts.append(body_pdf) + + # Convert attachments to PDF + if msg.attachments: + log.info(f" Has {len(msg.attachments)} attachment(s)") + for att in msg.attachments: + try: + att_pdf = convert_attachment_to_pdf( + att.payload, att.filename, att.content_type + ) + if att_pdf: + pdf_parts.append(att_pdf) + except Exception as e: + log.warning(f" Failed to convert attachment {att.filename}: {e}") + + # Merge all PDFs + if pdf_parts: + merged_pdf = merge_pdfs(pdf_parts) + + if merged_pdf: + # Save to domain folder + domain_folder = BASE_OUTPUT / sanitize_filename(match_domain, 50) + domain_folder.mkdir(parents=True, exist_ok=True) + + filepath_domain = domain_folder / filename + filepath_domain.write_bytes(merged_pdf) + + # Save to all folder + filepath_all = ALL_FOLDER / f"{match_domain}_{filename}" + filepath_all.write_bytes(merged_pdf) + + log.info(f" SAVED: {filename}") + total_processed += 1 + else: + log.warning(f" Failed to create merged PDF") + total_errors += 1 + else: + log.warning(f" No content to convert") + total_errors += 1 + + # Save progress + save_progress(uid, processed) + + except Exception as e: + # Check if it's a socket error — try to reconnect + if 'socket error' in str(e) or 'EOF' in str(e) or 'abort' in str(e): + log.warning(f"Connection lost, trying to reconnect...") + mailbox = reconnect_mailbox() + # Don't mark as processed, retry next time + total_errors += 1 + continue + else: + log.error(f" ERROR processing UID={uid}: {e}", exc_info=True) + total_errors += 1 + save_progress(uid, processed) + + mailbox.logout() + + # Summary + total_time = time.time() - start_time + summary_text = f""" +📦 ОТЧЕТ ПО ЭКСПОРТУ ПОЧТЫ (Mutt) + +📅 Дата: {datetime.now().strftime('%Y-%m-%d %H:%M')} +📧 Просканировано: {total_scanned} +🎯 Найдено (фильтр): {len(matching_uids)} +✅ Обработано (новых): {total_processed} +❌ Ошибок: {total_errors} +⏱️ Время: {total_time/60:.1f} мин. + +📁 Все файлы сохранены в PDF и готовы к загрузке в NotebookLM! +""" + log.info("\n" + "=" * 60) + log.info("EXPORT COMPLETE!") + log.info(f"Total scanned: {total_scanned}") + log.info(f"Matching: {len(matching_uids)}") + log.info(f"Processed this run: {total_processed}") + log.info(f"Errors: {total_errors}") + log.info(f"Total time: {total_time/60:.1f} minutes") + log.info("=" * 60) + + # Telegram Notification (only if processed > 0) + if total_processed > 0: + try: + import requests + BOT_TOKEN = "8725618164:AAH1tGalq-pw1l0t4P5c0sdLkCVJdh7IE7M" + CHAT_ID = "197957361" + url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage" + params = { + "chat_id": CHAT_ID, + "text": "📬 ВАЖНОЕ ОБНОВЛЕНИЕ ПОЧТЫ!\n" + summary_text + "\n🚀 Пора обновить источники в NotebookLM!", + "parse_mode": "HTML" + } + # Use proxy if needed + proxies = {"https": "socks5h://127.0.0.1:10808"} + requests.post(url, json=params, proxies=proxies, timeout=20) + log.info("✅ Telegram notification sent.") + except Exception as e: + log.error(f"❌ Failed to send Telegram notification: {e}") + +if __name__ == '__main__': + main() diff --git a/export_phase2.py b/export_phase2.py new file mode 100644 index 0000000..9efea5a --- /dev/null +++ b/export_phase2.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 +""" +Phase 2 only: Download matching UIDs and convert to PDF. +Reads UIDs from .export_progress (or scans fresh if not found). +Reconnects for each email to avoid Gmail IMAP drops. +""" + +import os +import sys +import re +import time +import logging +from pathlib import Path +from datetime import datetime, date + +from imap_tools import MailBox, AND +from weasyprint import HTML +from fpdf import FPDF +import fitz # PyMuPDF + +# === CONFIG === +IMAP_SERVER = "imap.gmail.com" +GMAIL_FOLDER = "[Gmail]/Вся почта" + +MUTT_CONF = Path(__file__).parent / ".muttrc" +if not MUTT_CONF.exists(): + MUTT_CONF = Path.home() / ".muttrc" + +DOMAINS = [ + "gosuslugi.ru", "dom.gosuslugi.ru", "gov.ru", "duma.gov.ru", + "mvd.gov.ru", "mvd.ru", "rkn.gov.ru", "nalog.gov.ru", "nalog.ru", + "genproc.gov.ru", "pfr.gov.ru", "fss.ru", "rospotrebnadzor.ru", + "russianpost.ru", "mailop.ru", "73.mailop.ru", "uk-service.ru", + "ulgss.ru", "ulgov.ru", "ulgkh.ru", "dgi.ru", "mos.ru", "pfrf.ru", + "fsin.gov.ru", "skrf.ru", "epp.genproc.gov.ru", "ugpr.ru", + "fas.gov.ru", "minstroyrf.gov.ru", "gkh.ru", "sudrf.ru", +] + +SINCE_DATE = date(2025, 12, 20) + +BASE_OUTPUT = Path(__file__).parent / "exported_emails" +ALL_FOLDER = BASE_OUTPUT / "all" +PROGRESS_FILE = Path(__file__).parent / ".export_progress" +PHASE2_PROGRESS = Path(__file__).parent / ".phase2_progress" + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler(Path(__file__).parent / "export.log", encoding="utf-8"), + logging.StreamHandler(sys.stdout), + ], +) +log = logging.getLogger(__name__) + + +def parse_mutt_config(): + config = {} + with open(MUTT_CONF) as f: + for line in f: + line = line.strip() + if line.startswith('#') or not line: + continue + match = re.match(r'set\s+(\w+)\s*=\s*"([^"]*)"', line) + if not match: + match = re.match(r'set\s+(\w+)\s*=\s*(\S+)', line) + if match: + config[match.group(1)] = match.group(2) + return config + + +def sanitize_filename(name, max_len=80): + name = re.sub(r'[<>:"/\\|?*\x00-\x1f]', '_', name) + name = re.sub(r'\s+', ' ', name).strip() + return name[:max_len] + + +def matches_domain(addr, domains): + if not addr: + return None + addr_lower = addr.lower() + for domain in domains: + if domain.lower() in addr_lower: + return domain + return None + + +def html_to_pdf_stream(html_content): + try: + return HTML(string=html_content).write_pdf() + except Exception as e: + log.warning(f"weasyprint failed: {e}") + return html_to_pdf_fpdf(html_content) + + +def html_to_pdf_fpdf(html_content): + from bs4 import BeautifulSoup + soup = BeautifulSoup(html_content, 'lxml') + text = soup.get_text(separator='\n') + pdf = FPDF() + pdf.add_page() + pdf.add_font('DejaVu', '', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', uni=True) + pdf.add_font('DejaVu', 'B', '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', uni=True) + pdf.set_font('DejaVu', '', 10) + lines = [line for line in text.split('\n') if line.strip()] + for line in lines: + pdf.multi_cell(0, 6, line) + pdf.ln(1) + return pdf.output() + + +def convert_attachment_to_pdf(data, filename, content_type): + ext = Path(filename).suffix.lower() + try: + if ext in ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp', '.svg'): + pdf = FPDF() + pdf.add_page() + import io + from PIL import Image + img = Image.open(io.BytesIO(data)) + w_px, h_px = img.size + w_mm = w_px * 25.4 / 96 + h_mm = h_px * 25.4 / 96 + page_w = pdf.w - 20 + scale = min(page_w / w_mm, (pdf.h - 20) / h_mm, 1.0) + import tempfile + with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmp: + tmp.write(data) + tmp_path = tmp.name + try: + pdf.image(tmp_path, x=10, y=10, w=w_mm*scale, h=h_mm*scale) + finally: + os.unlink(tmp_path) + return pdf.output() + + elif ext in ('.doc', '.docx', '.xls', '.xlsx'): + import tempfile, subprocess + with tempfile.TemporaryDirectory() as tmpdir: + src = Path(tmpdir) / filename + src.write_bytes(data) + subprocess.run( + ['libreoffice', '--headless', '--convert-to', 'pdf', + '--outdir', tmpdir, str(src)], + capture_output=True, timeout=120 + ) + pdf_path = src.with_suffix('.pdf') + if pdf_path.exists(): + return pdf_path.read_bytes() + + elif ext == '.pdf': + return data + + elif ext in ('.txt', '.csv', '.log', '.xml', '.html', '.htm'): + try: + text = data.decode('utf-8', errors='replace') + except: + text = data.decode('latin-1', errors='replace') + return html_to_pdf_fpdf(text) + + else: + info = f"Attachment: {filename}\nType: {content_type}\nSize: {len(data)} bytes\n\n[Binary content - not converted]" + return html_to_pdf_fpdf(info) + except Exception as e: + log.warning(f"Failed to convert {filename}: {e}") + info = f"Attachment: {filename}\nType: {content_type}\nSize: {len(data)} bytes\n\nConversion failed: {e}" + return html_to_pdf_fpdf(info) + + +def merge_pdfs(pdf_list): + if not pdf_list: + return None + if len(pdf_list) == 1: + return pdf_list[0] + merger = fitz.open() + for pdf_bytes in pdf_list: + try: + doc = fitz.open(stream=pdf_bytes, filetype="pdf") + merger.insert_pdf(doc) + except Exception as e: + log.warning(f"Failed to merge PDF section: {e}") + output = merger.tobytes() + merger.close() + return output + + +def build_email_html(msg): + subject = msg.subject or "(Без темы)" + from_addr = msg.from_ or "unknown" + to_addr = ", ".join(msg.to) if msg.to else "unknown" + cc_addr = ", ".join(msg.cc) if msg.cc else "" + date_str = msg.date_str or str(msg.date) if msg.date else "unknown" + + body_html = msg.html + if not body_html and msg.text: + text = msg.text + text_escaped = text.replace('&', '&').replace('<', '<').replace('>', '>') + body_html = text_escaped.replace('\n', '
    \n') + elif not body_html: + body_html = "

    (Нет тела письма)

    " + + att_info = "" + if msg.attachments: + items = [f'
  • {a.filename} ({a.content_type}, {a.size} bytes)
  • ' for a in msg.attachments] + att_info = '

    Вложения:

      ' + ''.join(items) + '
    ' + + return f""" + + +
    +

    {subject}

    + + + + {f'' if cc_addr else ''} + +
    От:{from_addr}
    Кому:{to_addr}
    Копия:{cc_addr}
    Дата:{date_str}
    +
    +
    {body_html}
    + {f'
    {att_info}
    ' if att_info else ''} + """ + + +def load_phase2_progress(): + """Load set of UIDs already converted to PDF.""" + if PHASE2_PROGRESS.exists(): + return set(PHASE2_PROGRESS.read_text().strip().splitlines()) + return set() + + +def save_phase2_progress(uid, processed_set): + processed_set.add(str(uid)) + PHASE2_PROGRESS.write_text("\n".join(processed_set)) + + +def main(): + log.info("=" * 60) + log.info("Phase 2: Download & Convert to PDF") + log.info("=" * 60) + + config = parse_mutt_config() + imap_user = config.get('imap_user', '') + imap_pass = config.get('imap_pass', '') + + if not imap_user or imap_user == 'your.email@gmail.com': + log.error("No credentials in .muttrc!") + sys.exit(1) + + BASE_OUTPUT.mkdir(parents=True, exist_ok=True) + ALL_FOLDER.mkdir(parents=True, exist_ok=True) + + # Load UIDs from phase1 progress + if not PROGRESS_FILE.exists(): + log.error("No .export_progress found. Run phase 1 first.") + sys.exit(1) + + all_uids = [line.strip() for line in PROGRESS_FILE.read_text().strip().splitlines() if line.strip()] + log.info(f"Total UIDs from phase 1: {len(all_uids)}") + + # Load phase 2 progress (already converted) + phase2_done = load_phase2_progress() + log.info(f"Already converted: {len(phase2_done)}") + + remaining = [uid for uid in all_uids if uid not in phase2_done] + log.info(f"Remaining to convert: {len(remaining)}") + + if not remaining: + log.info("All done!") + return + + start_time = time.time() + total_processed = 0 + total_errors = 0 + + for idx, uid in enumerate(remaining, 1): + elapsed = time.time() - start_time + if idx > 1: + eta = (elapsed / (idx - 1)) * (len(remaining) - idx + 1) + else: + eta = 0 + + log.info(f"[{idx}/{len(remaining)}] UID={uid} | ETA: {eta/60:.1f}m") + + # Reconnect for each email + mailbox = None + for attempt in range(3): + try: + mailbox = MailBox(IMAP_SERVER).login(imap_user, imap_pass, initial_folder=GMAIL_FOLDER) + break + except Exception as e: + log.warning(f"Connect attempt {attempt+1} failed: {e}") + time.sleep(5 * (attempt + 1)) + mailbox = None + + if not mailbox: + log.error(f"Failed to connect for UID={uid}") + total_errors += 1 + continue + + try: + msgs = list(mailbox.fetch( + AND(uid=uid), + headers_only=False, + mark_seen=False, + )) + if not msgs: + log.warning(f" Could not fetch UID={uid}") + total_errors += 1 + save_phase2_progress(uid, phase2_done) + continue + + msg = msgs[0] + subject = msg.subject or "(Без темы)" + from_addr = msg.from_ or "unknown" + + match_domain = matches_domain(from_addr, DOMAINS) + if not match_domain: + for to_addr in (msg.to or []): + match_domain = matches_domain(to_addr, DOMAINS) + if match_domain: + break + if not match_domain: + match_domain = "other" + + safe_subject = sanitize_filename(subject) + safe_from = sanitize_filename(from_addr.split('<')[-1].rstrip('>').split('@')[0], 30) + date_part = msg.date.strftime('%Y-%m-%d') if msg.date else 'unknown' + + filename = f"{date_part}_{safe_from}_{safe_subject}.pdf" + if len(filename) > 150: + filename = filename[:150] + ".pdf" + + html_content = build_email_html(msg) + pdf_parts = [] + + body_pdf = html_to_pdf_stream(html_content) + if body_pdf: + pdf_parts.append(body_pdf) + + if msg.attachments: + log.info(f" {len(msg.attachments)} attachment(s)") + for att in msg.attachments: + try: + att_pdf = convert_attachment_to_pdf( + att.payload, att.filename, att.content_type + ) + if att_pdf: + pdf_parts.append(att_pdf) + except Exception as e: + log.warning(f" Attachment convert failed {att.filename}: {e}") + + if pdf_parts: + merged_pdf = merge_pdfs(pdf_parts) + if merged_pdf: + domain_folder = BASE_OUTPUT / sanitize_filename(match_domain, 50) + domain_folder.mkdir(parents=True, exist_ok=True) + (domain_folder / filename).write_bytes(merged_pdf) + (ALL_FOLDER / f"{match_domain}_{filename}").write_bytes(merged_pdf) + log.info(f" SAVED: {filename}") + total_processed += 1 + else: + log.warning(f" Failed to merge PDF") + total_errors += 1 + else: + log.warning(f" No content") + total_errors += 1 + + save_phase2_progress(uid, phase2_done) + + except Exception as e: + log.error(f" ERROR UID={uid}: {e}") + total_errors += 1 + + finally: + if mailbox: + try: + mailbox.logout() + except: + pass + + # Small delay to avoid Gmail rate limiting + time.sleep(0.5) + + total_time = time.time() - start_time + log.info("\n" + "=" * 60) + log.info("PHASE 2 COMPLETE!") + log.info(f"Processed: {total_processed}") + log.info(f"Errors: {total_errors}") + log.info(f"Time: {total_time/60:.1f} min") + log.info(f"Total PDFs: {len(list(ALL_FOLDER.glob('*.pdf')))}") + log.info("=" * 60) + + +if __name__ == '__main__': + main() diff --git a/report_detailed.txt b/report_detailed.txt new file mode 100644 index 0000000..ab94839 --- /dev/null +++ b/report_detailed.txt @@ -0,0 +1,428 @@ +📊 **ПОЛНЫЙ ОТЧЁТ ПО ПЕРЕПИСКЕ С ОРГАНАМИ ВЛАСТИ** +📅 Дата формирования: 12.04.2026 18:48 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +──────────────────────────────────────────────── +**🟦 Госуслуги** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2025-12-20 │ ⏳ Нет ответа │ o-reply │ Примите участие в конкурсе «Лиде... +2 │ 2025-12-22 │ ⏳ Нет ответа │ o-reply │ Оцените работу социальных органи... +3 │ 2025-12-23 │ ⏳ Нет ответа │ o-reply │ Присоединяйтесь к чату вашего до... +4 │ 2025-12-25 │ ⏳ Нет ответа │ o-reply │ Re_ Присоединяйтесь к чату вашег... +5 │ 2025-12-27 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +6 │ 2025-12-29 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +7 │ 2025-12-30 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +8 │ 2025-12-30 │ ⏳ Нет ответа │ o-reply │ Изменены сведения в Реестре воин... +9 │ 2025-12-30 │ Формальная ⚠️ │ 73-2025-28074 │ ГИС ЖКХ. Взято в работу обращени... +10 │ 2025-12-31 │ ⏳ Нет ответа │ o-reply │ С наступающим Новым годом! +11 │ 2026-01-03 │ Формальная ⚠️ │ 73-2026-26 │ ГИС ЖКХ. Взято в работу обращени... +12 │ 2026-01-03 │ Формальная ⚠️ │ 73-2026-26 │ ГИС ЖКХ. Завершены работы по обр... +13 │ 2026-01-03 │ Формальная ⚠️ │ 73-2026-26 │ ГИС ЖКХ. Ответ на обращение № 73... +14 │ 2026-01-04 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +15 │ 2026-01-07 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +16 │ 2026-01-12 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +17 │ 2026-01-13 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +18 │ 2026-01-13 │ Формальная ⚠️ │ 73-2025-28128 │ ГИС ЖКХ. Взято в работу обращени... +19 │ 2026-01-13 │ Формальная ⚠️ │ 73-2026-681 │ ГИС ЖКХ. Взято в работу обращени... +20 │ 2026-01-14 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +21 │ 2026-01-14 │ Формальная ⚠️ │ 20260100104989 │ Доставлено информационное сообще... +22 │ 2026-01-16 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +23 │ 2026-01-16 │ ⏳ Нет ответа │ o-reply │ Новое голосование по вашему дому +24 │ 2026-01-18 │ Формальная ⚠️ │ o-reply │ Ваше обращение передано на испол... +25 │ 2026-01-18 │ Формальная ⚠️ │ o-reply │ Ваше обращение поступило на моде... +26 │ 2026-01-18 │ Формальная ⚠️ │ o-reply │ Ваше обращение принято модератором +27 │ 2026-01-18 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +28 │ 2026-01-19 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +29 │ 2026-01-19 │ Формальная ⚠️ │ 73-2026-1580 │ ГИС ЖКХ. Взято в работу обращени... +30 │ 2026-01-20 │ Формальная ⚠️ │ o-reply │ Ваше обращение рассмотрено +31 │ 2026-01-22 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +32 │ 2026-01-22 │ Формальная ⚠️ │ 73-2025-28074 │ ГИС ЖКХ. Завершены работы по обр... +33 │ 2026-01-22 │ Формальная ⚠️ │ 73-2026-681 │ ГИС ЖКХ. Завершены работы по обр... +34 │ 2026-01-22 │ Формальная ⚠️ │ 73-2025-28074 │ ГИС ЖКХ. Ответ на обращение № 73... +35 │ 2026-01-22 │ Формальная ⚠️ │ 73-2026-681 │ ГИС ЖКХ. Ответ на обращение № 73... +36 │ 2026-01-23 │ ⏳ Нет ответа │ o-reply │ «Цифровой марафон» от Сбера +37 │ 2026-01-24 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +38 │ 2026-01-25 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +39 │ 2026-01-26 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +40 │ 2026-01-26 │ ⏳ Нет ответа │ o-reply │ Началось собрание собственников ... +41 │ 2026-01-27 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +42 │ 2026-01-27 │ Формальная ⚠️ │ 73-2026-2441 │ ГИС ЖКХ. Взято в работу обращени... +43 │ 2026-01-27 │ Формальная ⚠️ │ 73-2026-2619 │ ГИС ЖКХ. Взято в работу обращени... +44 │ 2026-01-27 │ Формальная ⚠️ │ 73-2026-2645 │ ГИС ЖКХ. Взято в работу обращени... +45 │ 2026-01-27 │ Формальная ⚠️ │ 73-2025-28128 │ ГИС ЖКХ. Завершены работы по обр... +46 │ 2026-01-27 │ Формальная ⚠️ │ 73-2025-28128 │ ГИС ЖКХ. Ответ на обращение № 73... +47 │ 2026-01-28 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +48 │ 2026-01-28 │ Формальная ⚠️ │ 73-2026-2766 │ ГИС ЖКХ. Взято в работу обращени... +49 │ 2026-01-29 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +50 │ 2026-01-29 │ ⏳ Нет ответа │ o-reply │ Отзыв по процессу получения госу... +51 │ 2026-01-29 │ ⏳ Нет ответа │ o-reply │ Получение онлайн-выписки +52 │ 2026-01-29 │ Формальная ⚠️ │ 73-2026-2898 │ ГИС ЖКХ. Взято в работу обращени... +53 │ 2026-01-29 │ Формальная ⚠️ │ 73-2026-2900 │ ГИС ЖКХ. Взято в работу обращени... +54 │ 2026-01-30 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +55 │ 2026-01-30 │ Формальная ⚠️ │ 73-2026-2969 │ ГИС ЖКХ. Взято в работу обращени... +56 │ 2026-02-01 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +57 │ 2026-02-01 │ ⏳ Нет ответа │ o-reply │ Изменены сведения в Реестре воин... +58 │ 2026-02-02 │ Формальная ⚠️ │ 73-2026-3108 │ ГИС ЖКХ. Взято в работу обращени... +59 │ 2026-02-02 │ Формальная ⚠️ │ 73-2026-3294 │ ГИС ЖКХ. Взято в работу обращени... +60 │ 2026-02-02 │ Формальная ⚠️ │ 73-2026-3295 │ ГИС ЖКХ. Взято в работу обращени... +61 │ 2026-02-04 │ ⏳ Нет ответа │ o-reply │ Узнавайте важное о новостройках ... +62 │ 2026-02-05 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +63 │ 2026-02-05 │ ⏳ Нет ответа │ o-reply │ У вас есть непрочитанное сообщен... +64 │ 2026-02-06 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +65 │ 2026-02-06 │ ⏳ Нет ответа │ o-reply │ Присоединяйтесь к официальному ч... +66 │ 2026-02-06 │ Формальная ⚠️ │ 73-2026-4093 │ ГИС ЖКХ. Взято в работу обращени... +67 │ 2026-02-06 │ Формальная ⚠️ │ 20260200109025 │ Доставлено информационное сообще... +68 │ 2026-02-07 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +69 │ 2026-02-08 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +70 │ 2026-02-09 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +71 │ 2026-02-09 │ Формальная ⚠️ │ 73-2026-4257 │ ГИС ЖКХ. Взято в работу обращени... +72 │ 2026-02-09 │ Формальная ⚠️ │ 73-2026-4385 │ ГИС ЖКХ. Взято в работу обращени... +73 │ 2026-02-09 │ Формальная ⚠️ │ 73-2026-1580 │ ГИС ЖКХ. Завершены работы по обр... +74 │ 2026-02-09 │ Формальная ⚠️ │ 73-2026-2441 │ ГИС ЖКХ. Завершены работы по обр... +75 │ 2026-02-09 │ Формальная ⚠️ │ 73-2026-1580 │ ГИС ЖКХ. Ответ на обращение № 73... +76 │ 2026-02-09 │ Формальная ⚠️ │ 73-2026-2441 │ ГИС ЖКХ. Ответ на обращение № 73... +77 │ 2026-02-10 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +78 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2619 │ ГИС ЖКХ. Завершены работы по обр... +79 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2645 │ ГИС ЖКХ. Завершены работы по обр... +80 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2766 │ ГИС ЖКХ. Завершены работы по обр... +81 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2898 │ ГИС ЖКХ. Завершены работы по обр... +82 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-3108 │ ГИС ЖКХ. Завершены работы по обр... +83 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-3294 │ ГИС ЖКХ. Завершены работы по обр... +84 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-3295 │ ГИС ЖКХ. Завершены работы по обр... +85 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2619 │ ГИС ЖКХ. Ответ на обращение № 73... +86 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2645 │ ГИС ЖКХ. Ответ на обращение № 73... +87 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2766 │ ГИС ЖКХ. Ответ на обращение № 73... +88 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-2898 │ ГИС ЖКХ. Ответ на обращение № 73... +89 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-3108 │ ГИС ЖКХ. Ответ на обращение № 73... +90 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-3294 │ ГИС ЖКХ. Ответ на обращение № 73... +91 │ 2026-02-10 │ Формальная ⚠️ │ 73-2026-3295 │ ГИС ЖКХ. Ответ на обращение № 73... +92 │ 2026-02-11 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +93 │ 2026-02-11 │ Формальная ⚠️ │ 73-2026-4869 │ ГИС ЖКХ. Взято в работу обращени... +94 │ 2026-02-12 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +95 │ 2026-02-12 │ Формальная ⚠️ │ 73-2026-5028 │ ГИС ЖКХ. Взято в работу обращени... +96 │ 2026-02-12 │ Формальная ⚠️ │ 73-2026-5029 │ ГИС ЖКХ. Взято в работу обращени... +97 │ 2026-02-12 │ Формальная ⚠️ │ 73-2026-5075 │ ГИС ЖКХ. Взято в работу обращени... +98 │ 2026-02-13 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +99 │ 2026-02-13 │ Формальная ⚠️ │ 73-2026-4257 │ ГИС ЖКХ. Завершены работы по обр... +100 │ 2026-02-13 │ Формальная ⚠️ │ 73-2026-4257 │ ГИС ЖКХ. Ответ на обращение № 73... +101 │ 2026-02-14 │ ⏳ Нет ответа │ o-reply │ Новое голосование по вашему дому +102 │ 2026-02-15 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +103 │ 2026-02-15 │ ⏳ Нет ответа │ o-reply │ У вас есть непрочитанное сообщен... +104 │ 2026-02-16 │ Формальная ⚠️ │ o-reply │ Ваше обращение поступило на моде... +105 │ 2026-02-16 │ Формальная ⚠️ │ o-reply │ Ваше обращение принято модератором +106 │ 2026-02-16 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +107 │ 2026-02-16 │ Формальная ⚠️ │ o-reply │ Подача обращения в органы прокур... +108 │ 2026-02-17 │ Формальная ⚠️ │ o-reply │ Ваше обращение передано на испол... +109 │ 2026-02-17 │ Формальная ⚠️ │ o-reply │ Ваше обращение поступило на моде... +110 │ 2026-02-17 │ Формальная ⚠️ │ o-reply │ Ваше обращение принято модератором +111 │ 2026-02-17 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +112 │ 2026-02-17 │ ⏳ Нет ответа │ o-reply │ Отзыв по процессу получения госу... +113 │ 2026-02-17 │ Формальная ⚠️ │ 73-2026-5818 │ ГИС ЖКХ. Взято в работу обращени... +114 │ 2026-02-18 │ Формальная ⚠️ │ o-reply │ Ваше обращение взято в работу +115 │ 2026-02-18 │ Формальная ⚠️ │ o-reply │ Ваше обращение передано на испол... +116 │ 2026-02-18 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +117 │ 2026-02-18 │ ⏳ Нет ответа │ o-reply │ Оплатите госпошлину +118 │ 2026-02-19 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +119 │ 2026-02-19 │ Формальная ⚠️ │ 73-2026-6151 │ ГИС ЖКХ. Взято в работу обращени... +120 │ 2026-02-19 │ Формальная ⚠️ │ 73-2026-6152 │ ГИС ЖКХ. Взято в работу обращени... +121 │ 2026-02-20 │ Формальная ⚠️ │ o-reply │ Ваше обращение передано на испол... +122 │ 2026-02-20 │ Формальная ⚠️ │ o-reply │ Ваше обращение рассмотрено +123 │ 2026-02-24 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +124 │ 2026-02-24 │ ⏳ Нет ответа │ o-reply │ Началось собрание собственников ... +125 │ 2026-02-24 │ ⏳ Нет ответа │ o-reply │ Участвуйте в четвёртом сезоне «Б... +126 │ 2026-02-24 │ Формальная ⚠️ │ 20260200109025 │ Доставлено информационное сообще... +127 │ 2026-02-26 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +128 │ 2026-02-27 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +129 │ 2026-02-27 │ ⏳ Нет ответа │ o-reply │ Отзыв по процессу получения госу... +130 │ 2026-02-27 │ ⏳ Нет ответа │ o-reply │ Предоставление сведений из ЕГРН +131 │ 2026-02-27 │ Формальная ⚠️ │ 73-2026-2969 │ ГИС ЖКХ. Завершены работы по обр... +132 │ 2026-02-27 │ Формальная ⚠️ │ 73-2026-4869 │ ГИС ЖКХ. Завершены работы по обр... +133 │ 2026-02-27 │ Формальная ⚠️ │ 73-2026-5818 │ ГИС ЖКХ. Завершены работы по обр... +134 │ 2026-02-27 │ Формальная ⚠️ │ 73-2026-2969 │ ГИС ЖКХ. Ответ на обращение № 73... +135 │ 2026-02-27 │ Формальная ⚠️ │ 73-2026-4869 │ ГИС ЖКХ. Ответ на обращение № 73... +136 │ 2026-02-27 │ Формальная ⚠️ │ 73-2026-5818 │ ГИС ЖКХ. Ответ на обращение № 73... +137 │ 2026-03-01 │ ⏳ Нет ответа │ o-reply │ Блокировка абонентского номера +138 │ 2026-03-01 │ ⏳ Нет ответа │ o-reply │ Отзыв по процессу получения госу... +139 │ 2026-03-02 │ Формальная ⚠️ │ 73-2026-6151 │ ГИС ЖКХ. Завершены работы по обр... +140 │ 2026-03-02 │ Формальная ⚠️ │ 73-2026-6151 │ ГИС ЖКХ. Ответ на обращение № 73... +141 │ 2026-03-03 │ Формальная ⚠️ │ 73-2026-2900 │ ГИС ЖКХ. Завершены работы по обр... +142 │ 2026-03-03 │ Формальная ⚠️ │ 73-2026-2900 │ ГИС ЖКХ. Ответ на обращение № 73... +143 │ 2026-03-04 │ Формальная ⚠️ │ 73-2026-4093 │ ГИС ЖКХ. Завершены работы по обр... +144 │ 2026-03-04 │ Формальная ⚠️ │ 73-2026-5028 │ ГИС ЖКХ. Завершены работы по обр... +145 │ 2026-03-04 │ Формальная ⚠️ │ 73-2026-5075 │ ГИС ЖКХ. Завершены работы по обр... +146 │ 2026-03-04 │ Формальная ⚠️ │ 73-2026-4093 │ ГИС ЖКХ. Ответ на обращение № 73... +147 │ 2026-03-04 │ Формальная ⚠️ │ 73-2026-5028 │ ГИС ЖКХ. Ответ на обращение № 73... +148 │ 2026-03-04 │ Формальная ⚠️ │ 73-2026-5075 │ ГИС ЖКХ. Ответ на обращение № 73... +149 │ 2026-03-05 │ ⏳ Нет ответа │ o-reply │ Голосуйте за лучшие проекты прем... +150 │ 2026-03-05 │ Формальная ⚠️ │ 73-2026-4385 │ ГИС ЖКХ. Завершены работы по обр... +151 │ 2026-03-05 │ Формальная ⚠️ │ 73-2026-4385 │ ГИС ЖКХ. Ответ на обращение № 73... +152 │ 2026-03-06 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +153 │ 2026-03-06 │ ⏳ Нет ответа │ o-reply │ У вас есть непрочитанное сообщен... +154 │ 2026-03-06 │ ⏳ Нет ответа │ o-reply │ Электронное заказное письмо +155 │ 2026-03-06 │ Формальная ⚠️ │ 20260200109025 │ Доставлено информационное сообще... +156 │ 2026-03-06 │ Формальная ⚠️ │ 20260200109025 │ Доставлено информационное сообще... +157 │ 2026-03-07 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +158 │ 2026-03-07 │ ⏳ Нет ответа │ o-reply │ Ознакомьтесь с результатами голо... +159 │ 2026-03-08 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +160 │ 2026-03-10 │ Формальная ⚠️ │ 73-2026-5029 │ ГИС ЖКХ. Завершены работы по обр... +161 │ 2026-03-10 │ Формальная ⚠️ │ 73-2026-6152 │ ГИС ЖКХ. Завершены работы по обр... +162 │ 2026-03-10 │ Формальная ⚠️ │ 73-2026-5029 │ ГИС ЖКХ. Ответ на обращение № 73... +163 │ 2026-03-10 │ Формальная ⚠️ │ 73-2026-6152 │ ГИС ЖКХ. Ответ на обращение № 73... +164 │ 2026-03-11 │ ⏳ Нет ответа │ o-reply │ Проверьте выданные согласия на п... +165 │ 2026-03-15 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +166 │ 2026-03-16 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +167 │ 2026-03-17 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... +168 │ 2026-03-18 │ ⏳ Нет ответа │ o-reply │ Планируется отключение горячей воды +169 │ 2026-03-18 │ Формальная ⚠️ │ 73-2026-10010 │ ГИС ЖКХ. Взято в работу обращени... +170 │ 2026-03-18 │ Формальная ⚠️ │ 73-2026-10011 │ ГИС ЖКХ. Взято в работу обращени... +171 │ 2026-03-23 │ ⏳ Нет ответа │ o-reply │ Участвуйте в новом сезоне «Битвы... +172 │ 2026-03-24 │ ⏳ Нет ответа │ o-reply │ Присоединяйтесь к программам пом... +173 │ 2026-03-26 │ ⏳ Нет ответа │ o-reply │ Изменены сведения в Реестре воин... +174 │ 2026-03-27 │ ⏳ Нет ответа │ o-reply │ У вас есть непрочитанное сообщен... +175 │ 2026-03-27 │ Формальная ⚠️ │ 20260100104989 │ Доставлено информационное сообще... +176 │ 2026-03-31 │ ⏳ Нет ответа │ o-reply │ Электронное заказное письмо +177 │ 2026-04-01 │ ⏳ Нет ответа │ o-reply │ Вы авторизовались, используя учё... + +📊 Итого по органу: 177 писем | Ответов: 97 (55%) + +──────────────────────────────────────────────── +**🏛 УЛГСС** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2025-12-29 │ ⏳ Нет ответа │ — │ Test mail +2 │ 2025-12-29 │ Формальная ⚠️ │ 463-ФЗ │ Требование предоставить предложе... +3 │ 2025-12-30 │ ⏳ Нет ответа │ — │ ЛЕНКОМ 37 скан предложений по та... +4 │ 2025-12-30 │ ⏳ Нет ответа │ — │ Предложения жильцов-собственнико... +5 │ 2026-01-04 │ Формальная ⚠️ │ 2026 │ Запрос информации о статусе пред... +6 │ 2026-01-05 │ Формальная ⚠️ │ 2026 │ Запрос о работе погодника на Лен... +7 │ 2026-01-05 │ Формальная ⚠️ │ 2026 │ ТРЕБОВАНИЕ О ПРЕДОСТАВЛЕНИИ ИНФО... +8 │ 2026-01-06 │ Формальная ⚠️ │ 2026 │ О предоставлении информации о по... +9 │ 2026-01-07 │ Формальная ⚠️ │ 2026 │ Запрос информации о законности и... +10 │ 2026-01-07 │ Формальная ⚠️ │ 2026 │ Запрос о предоставлении Акта изм... +11 │ 2026-01-14 │ Формальная ⚠️ │ 2026 │ ПИСЬМО-УВЕДОМЛЕНИЕ (в ответ на в... +12 │ 2026-01-14 │ Формальная ⚠️ │ 2026 │ ТРЕБОВАНИЕ о предоставлении инфо... +13 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ ЗАПРОС о подтверждении полномочи... +14 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ О запросе информации касательно ... +15 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ О легальности размещения ТОС «Вз... +16 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ О нарушении процедуры созыва общ... +17 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ О непредоставлении проекта догов... +18 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ О несанкционированном ограничени... +19 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ О предоставлении договора с подр... +20 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ О противоречиях в расходовании с... +21 │ 2026-01-15 │ Формальная ⚠️ │ 2026 │ Об обосновании площади покоса га... +22 │ 2026-01-26 │ Формальная ⚠️ │ 2026 │ Акт выполненных работ (форма КС-... +23 │ 2026-01-26 │ Формальная ⚠️ │ 2026 │ Акт наладки погодника (ЛенКом 37) +24 │ 2026-01-26 │ Формальная ⚠️ │ 2026 │ О незаконном истребовании персон... +25 │ 2026-01-26 │ Формальная ⚠️ │ 2026 │ Об ограничении доступа к информа... +26 │ 2026-01-27 │ Формальная ⚠️ │ 2026 │ [REQ-2026-KS-TREE] Требование о ... +27 │ 2026-01-27 │ ⏳ Нет ответа │ 2026 │ [REQ-2026-PB-37] Запрос разрешит... +28 │ 2026-01-27 │ Формальная ⚠️ │ 2026 │ [REQ-2026-PODRYAD] Запрос копий ... +29 │ 2026-01-27 │ ⏳ Нет ответа │ 2026 │ [REQ-2026-REM5K] Запрос документ... +30 │ 2026-01-29 │ ⏳ Нет ответа │ 2026 │ (Без темы) +31 │ 2026-02-02 │ Формальная ⚠️ │ — │ ВОЗРАЖЕНИЕ на ответ № 217 от 28.... +32 │ 2026-02-24 │ Формальная ⚠️ │ — │ Уведомление о визите +33 │ 2026-03-10 │ Формальная ⚠️ │ — │ ТРЕБОВАНИЕ В АДРЕС ДИРЕКТОРА ООО... + +📊 Итого по органу: 33 писем | Ответов: 27 (82%) + +──────────────────────────────────────────────── +**🏢 УЛГКХ** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2026-01-03 │ Формальная ⚠️ │ 2026 │ Управляющая организация +2 │ 2026-01-13 │ Формальная ⚠️ │ 2026 │ Fwd_ Запрос информации о статусе... +3 │ 2026-01-13 │ Формальная ⚠️ │ 2026 │ Fwd_ ТРЕБОВАНИЕ О ПРЕДОСТАВЛЕНИИ... +4 │ 2026-01-14 │ Формальная ⚠️ │ 2026 │ Fwd_ Запрос информации о законно... +5 │ 2026-01-14 │ Формальная ⚠️ │ 2026 │ Fwd_ О предоставлении информации... +6 │ 2026-01-16 │ Формальная ⚠️ │ 2026 │ Fwd_ ПИСЬМО-УВЕДОМЛЕНИЕ (в ответ... +7 │ 2026-01-16 │ Формальная ⚠️ │ 2026 │ Fwd_ ТРЕБОВАНИЕ о предоставлении... +8 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ Запрос о предоставлении Акт... +9 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ Запрос о работе погодника н... +10 │ 2026-01-22 │ ⏳ Нет ответа │ 2026 │ Fwd_ ЛЕНКОМ 37 скан предложений ... +11 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ О легальности размещения ТО... +12 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ О непредоставлении проекта ... +13 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ О несанкционированном огран... +14 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ О предоставлении договора с... +15 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ О противоречиях в расходова... +16 │ 2026-01-22 │ Формальная ⚠️ │ 2026 │ Fwd_ Об обосновании площади поко... +17 │ 2026-01-26 │ Формальная ⚠️ │ 2026 │ Fwd_ О нарушении процедуры созыв... +18 │ 2026-01-27 │ Формальная ⚠️ │ 2026 │ Fwd_ Акт выполненных работ (форм... +19 │ 2026-01-27 │ Формальная ⚠️ │ 2026 │ Fwd_ ЗАПРОС о подтверждении полн... +20 │ 2026-01-27 │ Формальная ⚠️ │ 2026 │ Fwd_ О запросе информации касате... +21 │ 2026-01-28 │ Формальная ⚠️ │ 2026 │ Fwd_ Акт наладки погодника (ЛенК... +22 │ 2026-01-30 │ ⏳ Нет ответа │ 2026 │ Fwd_ [REQ-2026-PB-37] Запрос раз... +23 │ 2026-01-30 │ Формальная ⚠️ │ 2026 │ Fwd_ Акт выполненных работ (форм... +24 │ 2026-01-30 │ Формальная ⚠️ │ 2026 │ Fwd_ Об ограничении доступа к ин... +25 │ 2026-02-05 │ Формальная ⚠️ │ /pdf │ Fwd_ ВОЗРАЖЕНИЕ на ответ № 217 о... +26 │ 2026-02-06 │ Формальная ⚠️ │ 2026 │ Fwd_ О незаконном истребовании п... +27 │ 2026-02-24 │ Формальная ⚠️ │ — │ Fwd_ Уведомление о визите +28 │ 2026-02-25 │ Формальная ⚠️ │ — │ Дополнительный ответ +29 │ 2026-03-12 │ Формальная ⚠️ │ /pdf │ Fwd_ ТРЕБОВАНИЕ В АДРЕС ДИРЕКТОР... + +📊 Итого по органу: 29 писем | Ответов: 27 (93%) + +──────────────────────────────────────────────── +**🟧 Роспотребнадзор** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2026-01-18 │ Формальная ⚠️ │ o-reply │ Информация об обращении в Роспот... +2 │ 2026-01-22 │ Формальная ⚠️ │ 73-00-04/46-329... │ Ответ № 73-00-04_46-329-2026 от ... +3 │ 2026-01-25 │ Формальная ⚠️ │ o-reply │ Информация об обращении в Роспот... +4 │ 2026-01-26 │ Формальная ⚠️ │ 73-00-04/46-382... │ Ответ № 73-00-04_46-382-2026 от ... +5 │ 2026-02-08 │ Формальная ⚠️ │ o-reply │ Информация об обращении в Роспот... +6 │ 2026-02-12 │ По существу ✅ │ 73-00-04/46-857... │ Ответ № 73-00-04_46-857-2026 от ... +7 │ 2026-02-17 │ Формальная ⚠️ │ 73-00-04/46-991... │ Ответ № 73-00-04_46-991-2026 от ... +8 │ 2026-02-24 │ Формальная ⚠️ │ 73-00-04/46-113... │ Ответ № 73-00-04_46-1135-2026 от... +9 │ 2026-03-17 │ Формальная ⚠️ │ 73-00-04/46-169... │ Ответ № 73-00-04_46-1692-2026 от... + +📊 Итого по органу: 9 писем | Ответов: 9 (100%) + +──────────────────────────────────────────────── +**🟨 Почта России (mailop)** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2026-01-19 │ Формальная ⚠️ │ 2026 │ ответ на обращение +2 │ 2026-01-21 │ Формальная ⚠️ │ 2026 │ Ответ Тырину А.А. +3 │ 2026-01-28 │ Формальная ⚠️ │ 2026 │ Ответ Тырину А.А. +4 │ 2026-02-05 │ Формальная ⚠️ │ /pdf │ Уведомление по обращению +5 │ 2026-02-12 │ Формальная ⚠️ │ /pdf │ Уведомление по обращению +6 │ 2026-02-12 │ Формальная ⚠️ │ /pdf │ Ответ Тырину А.А. +7 │ 2026-02-17 │ Формальная ⚠️ │ /pdf │ Уведомление по обращению +8 │ 2026-02-19 │ Формальная ⚠️ │ /pdf │ Уведомление по обращению +9 │ 2026-02-24 │ Формальная ⚠️ │ /pdf │ Ответ Тырину А.А. + +📊 Итого по органу: 9 писем | Ответов: 9 (100%) + +──────────────────────────────────────────────── +**🟪 Правительство РФ** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2026-01-22 │ ⏳ Нет ответа │ o-reply │ Уведомление о регистрации обраще... +2 │ 2026-01-26 │ ⏳ Нет ответа │ 73-ИОГВ-18/790исх │ Исходящий N 73-ИОГВ-18_790исх от... +3 │ 2026-01-27 │ ⏳ Нет ответа │ 491-01/73 │ №491-01_73 от 27.01.2026_ Запрос... +4 │ 2026-01-28 │ Формальная ⚠️ │ 491-01/73 │ В ответ на № 491-01_73 +5 │ 2026-01-29 │ Формальная ⚠️ │ proc.gov.ru │ epp.genproc.gov.ru - Регистрация... +6 │ 2026-01-29 │ ⏳ Нет ответа │ 73-ИОГВ-18/938исх │ Исходящий N 73-ИОГВ-18_938исх от... +7 │ 2026-01-30 │ ⏳ Нет ответа │ o-reply │ Уведомление о регистрации обраще... +8 │ 2026-02-07 │ Формальная ⚠️ │ proc.gov.ru │ epp.genproc.gov.ru - Регистрация... +9 │ 2026-02-09 │ Формальная ⚠️ │ proc.gov.ru │ epp.genproc.gov.ru - Регистрация... +10 │ 2026-02-09 │ ⏳ Нет ответа │ 73-ИОГВ-18/1350исх │ Исходящий N 73-ИОГВ-18_1350исх о... +11 │ 2026-02-10 │ Формальная ⚠️ │ proc.gov.ru │ epp.genproc.gov.ru - Регистрация... +12 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1409исх │ Исходящий N 73-ИОГВ-18_1409исх о... +13 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1411исх │ Исходящий N 73-ИОГВ-18_1411исх о... +14 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1412исх │ Исходящий N 73-ИОГВ-18_1412исх о... +15 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1414исх │ Исходящий N 73-ИОГВ-18_1414исх о... +16 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1416исх │ Исходящий N 73-ИОГВ-18_1416исх о... +17 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1418исх │ Исходящий N 73-ИОГВ-18_1418исх о... +18 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1422исх │ Исходящий N 73-ИОГВ-18_1422исх о... +19 │ 2026-02-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/1424исх │ Исходящий N 73-ИОГВ-18_1424исх о... +20 │ 2026-02-11 │ ⏳ Нет ответа │ o-reply │ Уведомление о регистрации обраще... +21 │ 2026-02-13 │ ⏳ Нет ответа │ 73-ИОГВ-18/1669исх │ Исходящий N 73-ИОГВ-18_1669исх о... +22 │ 2026-02-16 │ ⏳ Нет ответа │ o-reply │ О регистрации обращения Тырина А... +23 │ 2026-02-16 │ Формальная ⚠️ │ proc.gov.ru │ epp.genproc.gov.ru - Регистрация... +24 │ 2026-02-16 │ ⏳ Нет ответа │ 73-ИОГВ-18/1737исх │ Исходящий N 73-ИОГВ-18_1737исх о... +25 │ 2026-02-17 │ ⏳ Нет ответа │ 1093-01/73 │ №1093-01_73 от 16.02.2026_ О нап... +26 │ 2026-02-19 │ ⏳ Нет ответа │ o-reply │ О регистрации обращения +27 │ 2026-02-20 │ ⏳ Нет ответа │ o-reply │ О регистрации обращения +28 │ 2026-02-20 │ ⏳ Нет ответа │ 1202-01/73 │ №1202-01_73 от 20.02.2026_ О про... +29 │ 2026-02-24 │ ⏳ Нет ответа │ 73-ИОГВ-18/2005исх │ Исходящий N 73-ИОГВ-18_2005исх о... +30 │ 2026-02-25 │ ⏳ Нет ответа │ 73-ИОГВ-18/2091исх │ Исходящий N 73-ИОГВ-18_2091исх о... +31 │ 2026-02-27 │ Формальная ⚠️ │ 1377-01/73 │ №1377-01_73 от 27.02.2026_ О рас... +32 │ 2026-02-27 │ ⏳ Нет ответа │ 73-ИОГВ-18.01/2... │ Исходящий N 73-ИОГВ-18.01_2299ис... +33 │ 2026-03-02 │ ⏳ Нет ответа │ o-reply │ О регистрации обращения 73-П-ОГ_... +34 │ 2026-03-02 │ ⏳ Нет ответа │ o-reply │ О регистрации обращения +35 │ 2026-03-02 │ Формальная ⚠️ │ o-reply │ Роскомнадзор предлагает оценить ... +36 │ 2026-03-02 │ ⏳ Нет ответа │ 73-ИОГВ-18/2338исх │ Исходящий N 73-ИОГВ-18_2338исх о... +37 │ 2026-03-04 │ ⏳ Нет ответа │ 73-ИОГВ-18/2490исх │ Исходящий N 73-ИОГВ-18_2490исх о... +38 │ 2026-03-04 │ ⏳ Нет ответа │ 73-ИОГВ-18/2491исх │ Исходящий N 73-ИОГВ-18_2491исх о... +39 │ 2026-03-04 │ ⏳ Нет ответа │ 73-ИОГВ-18/2492исх │ Исходящий N 73-ИОГВ-18_2492исх о... +40 │ 2026-03-04 │ ⏳ Нет ответа │ 73-ИОГВ-18/2528исх │ Исходящий N 73-ИОГВ-18_2528исх о... +41 │ 2026-03-04 │ ⏳ Нет ответа │ 73-ИОГВ-18/2532исх │ Исходящий N 73-ИОГВ-18_2532исх о... +42 │ 2026-03-05 │ ⏳ Нет ответа │ 73-ИОГВ-18/2644исх │ Исходящий N 73-ИОГВ-18_2644исх о... +43 │ 2026-03-06 │ Формальная ⚠️ │ 73-П-06/4793исх │ Исходящий N 73-П-06_4793исх от 0... +44 │ 2026-03-10 │ ⏳ Нет ответа │ 73-ИОГВ-18/2791исх │ Исходящий N 73-ИОГВ-18_2791исх о... +45 │ 2026-03-11 │ ⏳ Нет ответа │ 1644-01/73 │ №1644-01_73 от 11.03.2026_ О про... +46 │ 2026-03-13 │ Формальная ⚠️ │ 73-П-06/5345исх │ Исходящий N 73-П-06_5345исх от 1... +47 │ 2026-03-17 │ Формальная ⚠️ │ stroyrf.gov.ru │ 5800-ОГ_04_ +48 │ 2026-03-19 │ Формальная ⚠️ │ 73-П-06/5774исх │ Исходящий N 73-П-06_5774исх от 1... +49 │ 2026-03-19 │ ⏳ Нет ответа │ 73-ИОГВ-18/3247исх │ Исходящий N 73-ИОГВ-18_3247исх о... +50 │ 2026-03-20 │ Формальная ⚠️ │ 1925-01/73 │ №1925-01_73 от 20.03.2026_ О рас... +51 │ 2026-03-23 │ Формальная ⚠️ │ o-reply │ Роскомнадзор предлагает оценить ... +52 │ 2026-04-03 │ ⏳ Нет ответа │ 73-ИОГВ-18/3993исх │ Исходящий N 73-ИОГВ-18_3993исх о... + +📊 Итого по органу: 52 писем | Ответов: 14 (27%) + +──────────────────────────────────────────────── +**🟥 МВД России** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2026-01-27 │ Формальная ⚠️ │ o-reply │ sed_no-reply_Уведомление о стату... +2 │ 2026-01-30 │ Формальная ⚠️ │ o-reply │ sed_no-reply_Уведомление о стату... +3 │ 2026-02-06 │ Формальная ⚠️ │ o-reply │ sed_no-reply_Уведомление о стату... +4 │ 2026-02-17 │ Формальная ⚠️ │ o-reply │ sed_no-reply_Уведомление о стату... +5 │ 2026-02-18 │ Формальная ⚠️ │ o-reply │ sed_no-reply_Уведомление о стату... + +📊 Итого по органу: 5 писем | Ответов: 5 (100%) + +──────────────────────────────────────────────── +**📮 Почта России** +──────────────────────────────────────────────── + +№ │ Дата │ Статус │ Вх. № │ Тема +────┼────────────┼───────────────┼────────────────────┼─────────────────────────────── +1 │ 2026-01-30 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +2 │ 2026-01-31 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +3 │ 2026-02-02 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +4 │ 2026-02-02 │ ⏳ Нет ответа │ otification │ Предварительная запись в ОПС на ... +5 │ 2026-02-27 │ ⏳ Нет ответа │ otification │ Отправление EMS из Ульяновска +6 │ 2026-03-02 │ ⏳ Нет ответа │ otification │ Отправление EMS из Ульяновска +7 │ 2026-03-02 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +8 │ 2026-03-03 │ ⏳ Нет ответа │ otification │ Мелкий пакет из Китая +9 │ 2026-03-03 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +10 │ 2026-03-06 │ ⏳ Нет ответа │ otification │ Мелкий пакет из Китая +11 │ 2026-03-17 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +12 │ 2026-03-18 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +13 │ 2026-03-18 │ ⏳ Нет ответа │ otification │ Электронное извещение +14 │ 2026-03-19 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +15 │ 2026-03-20 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +16 │ 2026-03-21 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска +17 │ 2026-03-22 │ ⏳ Нет ответа │ otification │ Письмо из Ульяновска + +📊 Итого по органу: 17 писем | Ответов: 0 (0%) + +================================================ +📈 **СВОДНАЯ СТАТИСТИКА** +================================================ + +✉️ **Всего отправлено писем:** 331 +✅ **Получено ответов:** 188 (57%) +⏳ **Без ответа:** 143 (43%) + +📊 **КАЧЕСТВО ОТВЕТОВ:** + ⚠️ Формальные отписки: 187 (99%) + ✅ Ответы по существу: 1 (1%) + +🎯 **ВЫВОД:** + ⚠️ Преобладают формальные отписки. Требуется: + • Повторные обращения с требованием ответа по существу + • Жалобы в вышестоящие органы + • Обращение в прокуратуру на бездействие + +================================================ +🕐 Отчёт сформирован: 18:48 12.04.2026 +================================================ \ No newline at end of file diff --git a/report_table.txt b/report_table.txt new file mode 100644 index 0000000..1eee8bc --- /dev/null +++ b/report_table.txt @@ -0,0 +1,112 @@ +📊 **ОТЧЁТ ПО ПЕРЕПИСКЕ С ОРГАНАМИ** +📅 Дата формирования: 12.04.2026 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +📮 **ПО ОРГАНАМ:** + +**Госуслуги** +├─ Писем: 177 +├─ Ответов: 98 (55%) +├─ #175 | 2026-03-27 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ 20260100104989] +│ 📝 Доставлено информационное сообщение_ «Размеще... +├─ #176 | 2026-03-31 | ⏳ Нет ответа [Вх.№ o-reply] +│ 📝 Электронное заказное письмо... +├─ #177 | 2026-04-01 | ⏳ Нет ответа [Вх.№ o-reply] +│ 📝 Вы авторизовались, используя учётные данные Г... +└─ ... и ещё 174 писем + +**УЛГСС (Ульяновск)** +├─ Писем: 33 +├─ Ответов: 28 (85%) +├─ #31 | 2026-02-02 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ 217] +│ 📝 ВОЗРАЖЕНИЕ на ответ № 217 от 28.01.2026 г.... +├─ #32 | 2026-02-24 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ fo] +│ 📝 Уведомление о визите... +├─ #33 | 2026-03-10 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ fo] +│ 📝 ТРЕБОВАНИЕ В АДРЕС ДИРЕКТОРА ООО «УО ЖСС»... +└─ ... и ещё 30 писем + +**УЛГКХ (Ульяновск)** +├─ Писем: 29 +├─ Ответов: 28 (97%) +├─ #27 | 2026-02-24 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ fo] +│ 📝 Fwd_ Уведомление о визите... +├─ #28 | 2026-02-25 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ fo] +│ 📝 Дополнительный ответ... +├─ #29 | 2026-03-12 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ fo] +│ 📝 Fwd_ ТРЕБОВАНИЕ В АДРЕС ДИРЕКТОРА ООО «УО ЖСС... +└─ ... и ещё 26 писем + +**Роспотребнадзор** +├─ Писем: 9 +├─ Ответов: 5 (56%) +├─ #7 | 2026-02-17 | ⏳ Нет ответа [Вх.№ 73-00-04/46-991-2026] +│ 📝 Ответ № 73-00-04_46-991-2026 от 17.02.2026 на... +├─ #8 | 2026-02-24 | ⏳ Нет ответа [Вх.№ 73-00-04/46-1135-2026] +│ 📝 Ответ № 73-00-04_46-1135-2026 от 24.02.2026 н... +├─ #9 | 2026-03-17 | ⏳ Нет ответа [Вх.№ 73-00-04/46-1692-2026] +│ 📝 Ответ № 73-00-04_46-1692-2026 от 17.03.2026 н... +└─ ... и ещё 6 писем + +**Почта России (mailop)** +├─ Писем: 9 +├─ Ответов: 9 (100%) +├─ #7 | 2026-02-17 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ /pdf] +│ 📝 Уведомление по обращению... +├─ #8 | 2026-02-19 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ /pdf] +│ 📝 Уведомление по обращению... +├─ #9 | 2026-02-24 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ /pdf] +│ 📝 Ответ Тырину А.А.... +└─ ... и ещё 6 писем + +**Правительство РФ** +├─ Писем: 52 +├─ Ответов: 11 (21%) +├─ #50 | 2026-03-20 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ 1925-01/73] +│ 📝 №1925-01_73 от 20.03.2026_ О рассмотрении обр... +├─ #51 | 2026-03-23 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ o-reply] +│ 📝 Роскомнадзор предлагает оценить качество рабо... +├─ #52 | 2026-04-03 | ⏳ Нет ответа [Вх.№ 73-ИОГВ-18/3993исх] +│ 📝 Исходящий N 73-ИОГВ-18_3993исх от 03.04.2026 ... +└─ ... и ещё 49 писем + +**МВД России** +├─ Писем: 5 +├─ Ответов: 5 (100%) +├─ #3 | 2026-02-06 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ o-reply] +│ 📝 sed_no-reply_Уведомление о статусе обращения ... +├─ #4 | 2026-02-17 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ o-reply] +│ 📝 sed_no-reply_Уведомление о статусе обращения ... +├─ #5 | 2026-02-18 | ✅ Ответ → Формальная отписка ⚠ [Вх.№ o-reply] +│ 📝 sed_no-reply_Уведомление о статусе обращения ... +└─ ... и ещё 2 писем + +**Почта России** +├─ Писем: 17 +├─ Ответов: 1 (6%) +├─ #15 | 2026-03-20 | ⏳ Нет ответа [Вх.№ otification] +│ 📝 Письмо из Ульяновска... +├─ #16 | 2026-03-21 | ⏳ Нет ответа [Вх.№ otification] +│ 📝 Письмо из Ульяновска... +├─ #17 | 2026-03-22 | ⏳ Нет ответа [Вх.№ otification] +│ 📝 Письмо из Ульяновска... +└─ ... и ещё 14 писем + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +📈 **ИТОГО:** + +✉️ Всего отправлено: **331** +✅ Получено ответов: **185** (56%) +⏳ Без ответа: **146** + +📊 **КАЧЕСТВО ОТВЕТОВ:** + +⚠️ Формальные отписки: **181** (98%) +✅ Ответы по существу: **4** (2%) + +🎯 **ВЫВОД:** + +Преобладают **формальные отписки**. Требуется escalation. ⚠️ + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +🕐 Сформировано: 18:46 12.04.2026