🚀 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