103 lines
3.8 KiB
Python
103 lines
3.8 KiB
Python
# gmail_sender.py
|
||
import os
|
||
import pickle
|
||
import base64
|
||
from email.mime.multipart import MIMEMultipart
|
||
from email.mime.text import MIMEText
|
||
from email.mime.image import MIMEImage
|
||
from email.mime.application import MIMEApplication
|
||
from typing import List
|
||
from queue_manager import QueuedFile
|
||
from timezone_utils import format_time
|
||
import logging
|
||
import mimetypes
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Пути к файлам аутентификации
|
||
CREDENTIALS_FILE = 'credentials.json' # если нужен для обновления токена
|
||
TOKEN_FILE = 'token.pickle'
|
||
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
|
||
|
||
|
||
def get_gmail_service():
|
||
"""Загружает сохранённый токен и возвращает сервис Gmail API"""
|
||
creds = None
|
||
|
||
if not os.path.exists(TOKEN_FILE):
|
||
raise Exception(f"Файл {TOKEN_FILE} не найден. Загрузи его на сервер.")
|
||
|
||
with open(TOKEN_FILE, 'rb') as token:
|
||
creds = pickle.load(token)
|
||
|
||
# Если токен протух — обновляем (потребуется credentials.json)
|
||
from google.auth.transport.requests import Request
|
||
if creds and creds.expired and creds.refresh_token:
|
||
creds.refresh(Request())
|
||
# Сохраняем обновлённый токен
|
||
with open(TOKEN_FILE, 'wb') as token:
|
||
pickle.dump(creds, token)
|
||
|
||
from googleapiclient.discovery import build
|
||
return build('gmail', 'v1', credentials=creds)
|
||
|
||
|
||
async def send_batch_to_email(target_email: str, files: List[QueuedFile]):
|
||
"""Отправка письма через Gmail API (без SMTP)"""
|
||
if not target_email:
|
||
raise ValueError("Целевая почта не настроена")
|
||
|
||
if not files:
|
||
return
|
||
|
||
# Создаём письмо
|
||
msg = MIMEMultipart()
|
||
msg['To'] = target_email
|
||
msg['From'] = 'me' # 'me' = авторизованный аккаунт
|
||
msg['Subject'] = f"📎 {len(files)} новых файлов"
|
||
|
||
# Текст письма
|
||
body = f"Получено {len(files)} файлов.\n\n"
|
||
body += "Список вложений:\n"
|
||
body += "-" * 30 + "\n"
|
||
|
||
for idx, file in enumerate(files, 1):
|
||
file_size_kb = len(file.file_bytes) / 1024
|
||
if file_size_kb > 1024:
|
||
size_str = f"{file_size_kb / 1024:.2f} MB"
|
||
else:
|
||
size_str = f"{file_size_kb:.2f} KB"
|
||
body += f"{idx}. {file.filename} ({size_str})\n"
|
||
|
||
body += "\n---\n"
|
||
body += f"Время отправки: {format_time(files[0].timestamp)}"
|
||
|
||
msg.attach(MIMEText(body, 'plain'))
|
||
|
||
# Добавляем вложения
|
||
for file in files:
|
||
try:
|
||
mime_type, encoding = mimetypes.guess_type(file.filename)
|
||
|
||
if mime_type and mime_type.startswith('image/'):
|
||
img_type = mime_type.split('/')[1]
|
||
mime_file = MIMEImage(file.file_bytes, _subtype=img_type)
|
||
else:
|
||
mime_file = MIMEApplication(file.file_bytes, _subtype='octet-stream')
|
||
|
||
mime_file.add_header('Content-Disposition', 'attachment', filename=file.filename)
|
||
msg.attach(mime_file)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при добавлении вложения {file.filename}: {e}")
|
||
|
||
# Кодируем в base64 для API
|
||
raw_message = base64.urlsafe_b64encode(msg.as_bytes()).decode()
|
||
body_message = {'raw': raw_message}
|
||
|
||
# Отправляем через Gmail API
|
||
try:
|
||
service = get_gmail_service()
|
||
result = service.users().messages().send(userId='me', body=body_message).execute()
|
||
logger.info(f"✅ Письмо отправлено через Gmail API, ID: {result['id']}")
|
||
except Exception as e:
|
||
raise Exception(f"Gmail API ошибка: {str(e)}") |