Задача: автоматически забирать вложения из почтового ящика, сохранять их в папку и вести учёт — без нейросетей, только парсер.
Это первый шаг к AI-агенту для бухгалтерии. Сначала собираем документы, потом подключаем распознавание.
manifest.json хранит метаданные каждого файла:
{
"date": "2026-07-23",
"total": 5,
"files": [
{
"id": "inv_001",
"filename": "2026-07-23_10-15_ooo_tehno_invoice.pdf",
"path": "/tmp/invoices/files/2026-07-23_10-15_ooo_tehno_invoice.pdf",
"size": 245760,
"from": "ooo_tehno@mail.ru",
"subject": "Счет-фактура №СФ-0042",
"date": "2026-07-23T10:15:00",
"status": "pending"
}
]
}
Подключается по IMAP, забирает непрочитанные письма,
сохраняет вложения в папку и обновляет manifest.json.
#!/usr/bin/env python3
# parse_mail.py — парсер писем без нейросети
import imaplib
import email
import os
import json
import re
from datetime import datetime
from email.policy import default
# ====== КОНФИГ ======
IMAP_HOST = 'imap.yandex.ru'
EMAIL = 'your-email@yandex.ru'
PASSWORD = 'your-app-password'
BASE_DIR = '/tmp/invoices'
FILES_DIR = os.path.join(BASE_DIR, 'files')
MANIFEST_FILE = os.path.join(BASE_DIR, 'manifest.json')
os.makedirs(FILES_DIR, exist_ok=True)
# ====== ЗАГРУЖАЕМ MANIFEST ======
def load_manifest():
if os.path.exists(MANIFEST_FILE):
with open(MANIFEST_FILE, 'r') as f:
return json.load(f)
return {"date": datetime.now().strftime("%Y-%m-%d"), "total": 0, "files": []}
def save_manifest(manifest):
with open(MANIFEST_FILE, 'w') as f:
json.dump(manifest, f, indent=2, ensure_ascii=False)
# ====== БЕЗОПАСНОЕ ИМЯ ФАЙЛА ======
def safe_filename(from_addr, subject, date_str):
match = re.search(r'@([\w-]+)\.', from_addr)
org = match.group(1) if match else 'unknown'
clean_subject = re.sub(r'[^\w\s-]', '', subject).replace(' ', '_')[:30]
return f"{date_str}_{org}_{clean_subject}.pdf"
# ====== ПАРСИМ ПОЧТУ ======
def parse_mail():
print("📬 Подключаемся к почте...")
mail = imaplib.IMAP4_SSL(IMAP_HOST)
mail.login(EMAIL, PASSWORD)
mail.select('inbox')
since_date = datetime.now().strftime("%d-%b-%Y")
status, messages = mail.search(None, f'(UNSEEN SINCE "{since_date}")')
if status != 'OK' or not messages[0]:
print("❌ Нет новых писем за сутки")
mail.close()
mail.logout()
return
manifest = load_manifest()
new_count = 0
for msg_id in messages[0].split():
_, data = mail.fetch(msg_id, '(RFC822)')
raw = data[0][1]
msg = email.message_from_bytes(raw, policy=default)
from_addr = msg['From']
subject = msg['Subject'] or 'no_subject'
date_str = datetime.now().strftime("%Y-%m-%d_%H-%M")
print(f"📨 От: {from_addr}")
print(f"📌 Тема: {subject}")
for part in msg.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get_content_disposition() is None:
continue
filename = part.get_filename()
if filename:
ext = filename.split('.')[-1].lower()
if ext in ['pdf', 'jpg', 'jpeg', 'png']:
safe_name = safe_filename(from_addr, subject, date_str)
filepath = os.path.join(FILES_DIR, safe_name)
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
file_info = {
"id": f"inv_{len(manifest['files']) + 1:04d}",
"filename": safe_name,
"path": filepath,
"size": os.path.getsize(filepath),
"from": from_addr,
"subject": subject,
"date": datetime.now().isoformat(),
"status": "pending"
}
manifest['files'].append(file_info)
new_count += 1
print(f" 💾 Сохранен: {safe_name}")
manifest['total'] = len(manifest['files'])
save_manifest(manifest)
print(f"✅ Сохранено файлов: {new_count}")
mail.close()
mail.logout()
if __name__ == '__main__':
parse_mail()
Для Яндекса / Google нужно создать пароль приложения, а не использовать основной пароль.
python3 parse_mail.py
*/5 * * * * /usr/bin/python3 /usr/local/bin/parse_mail.py >> /var/log/parse_mail.log 2>&1
Когда парсер соберёт файлы, можно подключить распознавание:
manifest.jsonstatus: "pending" вызываем VL-модельstatus: "done" или "failed"
Комментарии
Пока нет комментариев. Будьте первым!