🚀 Initial commit: Mutt Email-to-AI Pipeline
This commit is contained in:
commit
e48d05eac3
9 changed files with 2183 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
*.pdf
|
||||||
|
*.log
|
||||||
|
.export_progress
|
||||||
|
.phase2_progress
|
||||||
|
exported_emails/
|
||||||
74
.muttrc
Normal file
74
.muttrc
Normal file
|
|
@ -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 "<change-folder>[Gmail]/All Mail<enter>" "Go to Gmail All Mail"
|
||||||
|
macro index,pager S "<change-folder>[Gmail]/Sent Mail<enter>" "Go to Sent"
|
||||||
|
macro index,pager D "<change-folder>[Gmail]/Drafts<enter>" "Go to Drafts"
|
||||||
|
macro index,pager T "<change-folder>[Gmail]/Trash<enter>" "Go to Trash"
|
||||||
|
macro index,pager A "<change-folder>[Gmail]/Spam<enter>" "Go to Spam"
|
||||||
227
analyze_emails.py
Normal file
227
analyze_emails.py
Normal file
|
|
@ -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}")
|
||||||
270
analyze_emails_detailed.py
Normal file
270
analyze_emails_detailed.py
Normal file
|
|
@ -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}")
|
||||||
86
consolidate_to_mega_pdf.py
Executable file
86
consolidate_to_mega_pdf.py
Executable file
|
|
@ -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"""
|
||||||
|
🔥 <b>ВАЖНО: СВОДНЫЙ ФАЙЛ ПОЧТЫ ОБНОВЛЕН!</b> 🔥
|
||||||
|
|
||||||
|
📑 <b>Файл:</b> <code>ALL_EMAILS_CONSOLIDATED.pdf</code>
|
||||||
|
📧 <b>Всего писем в архиве:</b> {count}
|
||||||
|
🐘 <b>Размер файла:</b> {size_mb:.2f} МБ
|
||||||
|
📅 <b>Обновлено:</b> {datetime.now().strftime('%Y-%m-%d %H:%M')}
|
||||||
|
|
||||||
|
🚀 <b>СРОЧНО:</b> Загрузи этот файл в Google Drive и обнови источники в <b>NotebookLM</b>!
|
||||||
|
<i>Старые данные AI должен "забыть", новые — осознать.</i>
|
||||||
|
"""
|
||||||
|
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()
|
||||||
577
export_emails.py
Normal file
577
export_emails.py
Normal file
|
|
@ -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', '<br>\n')
|
||||||
|
elif not body_html:
|
||||||
|
body_html = "<p><i>(Нет тела письма)</i></p>"
|
||||||
|
|
||||||
|
attachments_info = ""
|
||||||
|
if msg.attachments:
|
||||||
|
att_items = []
|
||||||
|
for att in msg.attachments:
|
||||||
|
att_items.append(f'<li>{att.filename} ({att.content_type}, {att.size} bytes)</li>')
|
||||||
|
attachments_info = '<h3 style="color:#555;">Вложения:</h3><ul>' + ''.join(att_items) + '</ul>'
|
||||||
|
|
||||||
|
html = f"""
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
body {{ font-family: 'DejaVu Sans', Arial, sans-serif; font-size: 12px; margin: 20px; color: #333; }}
|
||||||
|
.header {{ background: #f0f4f8; padding: 15px; border-radius: 5px; margin-bottom: 15px; border-left: 4px solid #3b82f6; }}
|
||||||
|
.header h2 {{ margin: 0 0 10px 0; font-size: 16px; color: #1e40af; }}
|
||||||
|
.header table {{ width: 100%; }}
|
||||||
|
.header td {{ padding: 3px 5px; vertical-align: top; }}
|
||||||
|
.header td:first-child {{ font-weight: bold; width: 80px; color: #555; }}
|
||||||
|
.body {{ line-height: 1.6; }}
|
||||||
|
.attachments {{ margin-top: 20px; padding: 10px; background: #f9fafb; border-radius: 3px; }}
|
||||||
|
.attachments li {{ margin: 3px 0; color: #666; }}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<h2>{subject}</h2>
|
||||||
|
<table>
|
||||||
|
<tr><td>От:</td><td>{from_addr}</td></tr>
|
||||||
|
<tr><td>Кому:</td><td>{to_addr}</td></tr>
|
||||||
|
{f'<tr><td>Копия:</td><td>{cc_addr}</td></tr>' if cc_addr else ''}
|
||||||
|
<tr><td>Дата:</td><td>{date_str}</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
{body_html}
|
||||||
|
</div>
|
||||||
|
{f'<div class="attachments">{attachments_info}</div>' if attachments_info else ''}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
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"""
|
||||||
|
📦 <b>ОТЧЕТ ПО ЭКСПОРТУ ПОЧТЫ (Mutt)</b>
|
||||||
|
|
||||||
|
📅 <b>Дата:</b> {datetime.now().strftime('%Y-%m-%d %H:%M')}
|
||||||
|
📧 <b>Просканировано:</b> {total_scanned}
|
||||||
|
🎯 <b>Найдено (фильтр):</b> {len(matching_uids)}
|
||||||
|
✅ <b>Обработано (новых):</b> {total_processed}
|
||||||
|
❌ <b>Ошибок:</b> {total_errors}
|
||||||
|
⏱️ <b>Время:</b> {total_time/60:.1f} мин.
|
||||||
|
|
||||||
|
📁 <i>Все файлы сохранены в PDF и готовы к загрузке в NotebookLM!</i>
|
||||||
|
"""
|
||||||
|
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": "📬 <b>ВАЖНОЕ ОБНОВЛЕНИЕ ПОЧТЫ!</b>\n" + summary_text + "\n🚀 <i>Пора обновить источники в NotebookLM!</i>",
|
||||||
|
"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()
|
||||||
404
export_phase2.py
Normal file
404
export_phase2.py
Normal file
|
|
@ -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', '<br>\n')
|
||||||
|
elif not body_html:
|
||||||
|
body_html = "<p><i>(Нет тела письма)</i></p>"
|
||||||
|
|
||||||
|
att_info = ""
|
||||||
|
if msg.attachments:
|
||||||
|
items = [f'<li>{a.filename} ({a.content_type}, {a.size} bytes)</li>' for a in msg.attachments]
|
||||||
|
att_info = '<h3 style="color:#555;">Вложения:</h3><ul>' + ''.join(items) + '</ul>'
|
||||||
|
|
||||||
|
return f"""
|
||||||
|
<html><head><meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
body {{ font-family: 'DejaVu Sans', Arial, sans-serif; font-size: 12px; margin: 20px; color: #333; }}
|
||||||
|
.header {{ background: #f0f4f8; padding: 15px; border-radius: 5px; margin-bottom: 15px; border-left: 4px solid #3b82f6; }}
|
||||||
|
.header h2 {{ margin: 0 0 10px 0; font-size: 16px; color: #1e40af; }}
|
||||||
|
.header table {{ width: 100%; }}
|
||||||
|
.header td {{ padding: 3px 5px; vertical-align: top; }}
|
||||||
|
.header td:first-child {{ font-weight: bold; width: 80px; color: #555; }}
|
||||||
|
.body {{ line-height: 1.6; }}
|
||||||
|
.attachments {{ margin-top: 20px; padding: 10px; background: #f9fafb; border-radius: 3px; }}
|
||||||
|
.attachments li {{ margin: 3px 0; color: #666; }}
|
||||||
|
</style></head><body>
|
||||||
|
<div class="header">
|
||||||
|
<h2>{subject}</h2>
|
||||||
|
<table>
|
||||||
|
<tr><td>От:</td><td>{from_addr}</td></tr>
|
||||||
|
<tr><td>Кому:</td><td>{to_addr}</td></tr>
|
||||||
|
{f'<tr><td>Копия:</td><td>{cc_addr}</td></tr>' if cc_addr else ''}
|
||||||
|
<tr><td>Дата:</td><td>{date_str}</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="body">{body_html}</div>
|
||||||
|
{f'<div class="attachments">{att_info}</div>' if att_info else ''}
|
||||||
|
</body></html>"""
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
428
report_detailed.txt
Normal file
428
report_detailed.txt
Normal file
|
|
@ -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
|
||||||
|
================================================
|
||||||
112
report_table.txt
Normal file
112
report_table.txt
Normal file
|
|
@ -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
|
||||||
Loading…
Reference in a new issue