#!/usr/bin/env python3 import os import sys import re import fitz # PyMuPDF from datetime import date, datetime from pathlib import Path import requests from weasyprint import HTML # Telegram settings BOT_TOKEN = "8725618164:AAH1tGalq-pw1l0t4P5c0sdLkCVJdh7IE7M" ADMIN_USER_ID = "197957361" # Domain mapping for human-readable naming DOMAINS_MAP = { "gosuslugi.ru": "Госуслуги РФ", "dom.gosuslugi.ru": "ГИС ЖКХ", "sudrf.ru": "Судебная система РФ", "zavolgskiy.uln@sudrf.ru": "Заволжский районный суд", "ulgss.ru": "УО Жилстройсервис", "ulgkh.ru": "Департамент ЖКХ", "fssp.gov.ru": "ФССП России", "fssprus.ru": "ФССП России", "gov.ru": "Правительство РФ", "ulgov.ru": "Администрация Ульяновской обл.", "russianpost.ru": "Почта России", "rkn.gov.ru": "Роскомнадзор", "nalog.gov.ru": "ФНС России", "nalog.ru": "ФНС России", "genproc.gov.ru": "Прокуратура РФ", "epp.genproc.gov.ru": "Прокуратура РФ", "mvd.gov.ru": "МВД России", "mvd.ru": "МВД России", "rospotrebnadzor.ru": "Роспотребнадзор", "pfr.gov.ru": "ПФР / СФР России", "pfrf.ru": "ПФР / СФР России", "fss.ru": "Фонд социального страхования", "sledcom.ru": "Следственный комитет РФ", "vkks.ru": "ВККС РФ", "mailop.ru": "Почтовый шлюз Mailop", "73.mailop.ru": "Почтовый шлюз 73.Mailop", "uk-service.ru": "УК Сервис-М", "dgi.ru": "Департамент гос. имущества", "mos.ru": "Портал Москвы mos.ru", "fsin.gov.ru": "ФСИН России", "skrf.ru": "Следственный комитет РФ", "ugpr.ru": "Управление госзакупок", "fas.gov.ru": "ФАС России", "minstroyrf.gov.ru": "Минстрой РФ", "gkh.ru": "Портал ЖКХ", "msud.ru": "Мировые судьи РФ", "arbitr.ru": "Арбитражные суды РФ", "mirsud": "Мировые судьи", "msud73.ru": "Мировые судьи Ульяновской обл.", "mirsud73.ru": "Мировые судьи Ульяновской обл.", "court.ru": "Судебный департамент РФ", "sud.ru": "Судебный департамент РФ", "uloblsud.ru": "Ульяновский областной суд", "duma.gov.ru": "Государственная Дума РФ", } def send_telegram_photo(photo_bytes, caption): url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendPhoto" files = { "photo": ("stats.png", photo_bytes, "image/png") } data = { "chat_id": ADMIN_USER_ID, "caption": caption, "parse_mode": "HTML" } print("Отправка отчета в Telegram...") try: r = requests.post(url, data=data, files=files, timeout=30) if r.status_code == 200: print("🎉 Картинка со статистикой успешно отправлена!") else: print(f"❌ Ошибка отправки: {r.status_code} - {r.text}") except Exception as e: print(f"❌ Ошибка отправки запроса в Telegram: {e}") def main(): folder = Path("/home/matrixhasyou/mutt/exported_emails/all") if not folder.exists(): print(f"Папка не существует: {folder}") sys.exit(1) pdf_files = list(folder.glob("*.pdf")) print(f"Всего PDF файлов найдено: {len(pdf_files)}") stats = {} # Summary counters total_in_mails = 0 total_in_pages = 0 total_in_atts = 0 total_out_mails = 0 total_out_pages = 0 total_out_atts = 0 # Process files for filepath in pdf_files: filename = filepath.name # Parse filename # Format is typically: {match_domain}_{date_part}_{safe_from}_{safe_subject}.pdf # Let's extract date date_match = re.search(r'_(\d{4}-\d{2}-\d{2})_', filename) if not date_match: # Maybe it starts with date: YYYY-MM-DD_domain_... date_match = re.match(r'^(\d{4}-\d{2}-\d{2})_', filename) if not date_match: continue date_str = date_match.group(1) if not date_str.startswith("2026"): continue # Parse domain and sender parts = filename.split("_", 2) if len(parts) >= 2: # If domain first domain = parts[0] # Verify if it looks like domain if not ("." in domain or "судебное" in domain or "other" in domain): # Fallback to general parsing domain = "other" else: domain = "other" try: doc = fitz.open(filepath) num_pages = len(doc) first_page_text = doc[0].get_text() # Determine direction (inbound / outbound) is_outbound = False first_page_lower = first_page_text.lower() # Heuristics if "от: ktybdsq@gmail.com" in first_page_lower or "from: ktybdsq@gmail.com" in first_page_lower: is_outbound = True elif "_ktybdsq_" in filename.lower(): is_outbound = True elif "fwd_" in filename.lower() and "от: ktybdsq@gmail.com" in first_page_lower: is_outbound = True # Attachment count att_count = 0 att_match = re.search(r"(?:Вложения|Attachments):(.*)", first_page_text, re.DOTALL | re.IGNORECASE) if att_match: block_text = att_match.group(1) # Count bytes occurrences as an indicator of attachments att_count = len(re.findall(r"bytes\)", block_text)) doc.close() # Add to stats if domain not in stats: stats[domain] = { 'inbound': {'mails': 0, 'pages': 0, 'atts': 0}, 'outbound': {'mails': 0, 'pages': 0, 'atts': 0} } direction = 'outbound' if is_outbound else 'inbound' stats[domain][direction]['mails'] += 1 stats[domain][direction]['pages'] += num_pages stats[domain][direction]['atts'] += att_count if is_outbound: total_out_mails += 1 total_out_pages += num_pages total_out_atts += att_count else: total_in_mails += 1 total_in_pages += num_pages total_in_atts += att_count except Exception as e: print(f"Ошибка парсинга {filename}: {e}") # Group results to consolidate subdomains or same entities grouped_stats = {} for dom, data in stats.items(): mapped_name = DOMAINS_MAP.get(dom, dom) # General groupings for cleaner output if "sudrf.ru" in dom or "mirsud" in dom or "msud" in dom or "court" in dom or "arbitr" in dom: mapped_name = "Судебная система РФ" elif "gosuslugi.ru" in dom: mapped_name = "Портал Госуслуг РФ" elif "gov.ru" in dom or "ulgov.ru" in dom: mapped_name = "Органы власти / Ведомства" elif "fssp.gov.ru" in dom or "fssprus.ru" in dom: mapped_name = "ФССП (Судебные приставы)" elif "nalog.ru" in dom or "nalog.gov.ru" in dom: mapped_name = "ФНС (Налоговая служба)" if mapped_name not in grouped_stats: grouped_stats[mapped_name] = { 'inbound': {'mails': 0, 'pages': 0, 'atts': 0}, 'outbound': {'mails': 0, 'pages': 0, 'atts': 0} } for dir_key in ['inbound', 'outbound']: for val_key in ['mails', 'pages', 'atts']: grouped_stats[mapped_name][dir_key][val_key] += data[dir_key][val_key] # Sort domains by total mail activity sorted_domains = sorted( grouped_stats.keys(), key=lambda k: grouped_stats[k]['inbound']['mails'] + grouped_stats[k]['outbound']['mails'], reverse=True ) # Render HTML to PDF and PNG html_content = """

⚖️ ОФИЦИАЛЬНАЯ ПЕРЕПИСКА С ВЕДОМСТВАМИ (2026 Г.)

Анализ входящих и исходящих официальных документов и страниц
Период: 01.01.2026 - """ + date.today().strftime('%d.%m.%Y') + """
""" for dom in sorted_domains: in_m = grouped_stats[dom]['inbound']['mails'] in_a = grouped_stats[dom]['inbound']['atts'] in_p = grouped_stats[dom]['inbound']['pages'] out_m = grouped_stats[dom]['outbound']['mails'] out_a = grouped_stats[dom]['outbound']['atts'] out_p = grouped_stats[dom]['outbound']['pages'] if in_m == 0 and out_m == 0: continue html_content += f""" """ html_content += f"""
Ведомство / Организация 📥 Входящие (мне) 📤 Исходящие (от меня)
Писем Влож. Стр. Писем Влож. Стр.
{dom} {in_m if in_m > 0 else '-'} {in_a if in_a > 0 else '-'} {in_p if in_p > 0 else '-'} {out_m if out_m > 0 else '-'} {out_a if out_a > 0 else '-'} {out_p if out_p > 0 else '-'}
ИТОГО: {total_in_mails} {total_in_atts} {total_in_pages} {total_out_mails} {total_out_atts} {total_out_pages}
📊 Сводные данные
Всего писем
{total_in_mails + total_out_mails}
Всего файлов / вложений
{total_in_atts + total_out_atts} шт.
Общий объем документов
{total_in_pages + total_out_pages} стр.
Входящий поток
{total_in_mails} писем / {total_in_pages} стр.
Исходящий поток
{total_out_mails} писем / {total_out_pages} стр.
""" # Render HTML to PDF using WeasyPrint print("Рендеринг HTML отчета в PDF...") pdf_bytes = HTML(string=html_content).write_pdf() # Convert PDF to PNG print("Конвертация PDF в PNG...") doc = fitz.open(stream=pdf_bytes, filetype="pdf") page = doc[0] pix = page.get_pixmap(dpi=180) png_bytes = pix.tobytes("png") doc.close() # Save the output PNG image out_png_path = Path("/home/matrixhasyou/mutt/email_stats_2026.png") out_png_path.write_bytes(png_bytes) print(f"✅ Итоговый график сохранен: {out_png_path}") # Send to Telegram caption = ( f"📊 Статистика переписки с ведомствами за весь 2026 год\n\n" f"🔹 Всего писем: {total_in_mails + total_out_mails}\n" f"📥 Входящих: {total_in_mails} (вложений: {total_in_atts}, страниц: {total_in_pages})\n" f"📤 Исходящих: {total_out_mails} (вложений: {total_out_atts}, страниц: {total_out_pages})\n" f"📁 Общий объем архива: {total_in_pages + total_out_pages} страниц" ) send_telegram_photo(png_bytes, caption) if __name__ == "__main__": main()