#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
SMTP邮件发送模块
支持发送纯文本、HTML格式邮件,支持附件
"""
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.header import Header
from email import encoders
import os
from typing import List, Optional, Dict, Any
class EmailSender:
"""SMTP邮件发送类"""
# 默认发送者配置(可以从环境变量或配置文件读取)
DEFAULT_CONFIG = {
'smtp_server': os.getenv('SMTP_SERVER', ''),
'smtp_port': int(os.getenv('SMTP_PORT', '25')),
'sender_email': os.getenv('SENDER_EMAIL', ''),
'sender_password': os.getenv('SENDER_PASSWORD', ''),
'use_tls': os.getenv('USE_TLS', 'false').lower() == 'true',
'use_ssl': os.getenv('USE_SSL', 'false').lower() == 'true'
}
@classmethod
def load_config_from_file(cls, config_file: str = 'email_config.json'):
"""
从配置文件加载配置
Args:
config_file: 配置文件路径
"""
import json
try:
if os.path.exists(config_file):
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
cls.DEFAULT_CONFIG.update(config)
print(f"已从 {config_file} 加载配置")
except Exception as e:
print(f"加载配置文件失败: {e}")
@classmethod
def set_default_config(
cls,
smtp_server: str = None,
smtp_port: int = None,
sender_email: str = None,
sender_password: str = None,
use_tls: bool = None,
use_ssl: bool = None
):
"""
设置默认配置
Args:
smtp_server: SMTP服务器地址
smtp_port: SMTP端口
sender_email: 发件人邮箱
sender_password: 发件人密码或授权码
use_tls: 是否使用TLS
use_ssl: 是否使用SSL
"""
if smtp_server is not None:
cls.DEFAULT_CONFIG['smtp_server'] = smtp_server
if smtp_port is not None:
cls.DEFAULT_CONFIG['smtp_port'] = smtp_port
if sender_email is not None:
cls.DEFAULT_CONFIG['sender_email'] = sender_email
if sender_password is not None:
cls.DEFAULT_CONFIG['sender_password'] = sender_password
if use_tls is not None:
cls.DEFAULT_CONFIG['use_tls'] = use_tls
if use_ssl is not None:
cls.DEFAULT_CONFIG['use_ssl'] = use_ssl
@classmethod
def get_default_config(cls) -> Dict[str, Any]:
"""获取默认配置"""
return cls.DEFAULT_CONFIG.copy()
def __init__(
self,
smtp_server: str = None,
smtp_port: int = None,
sender_email: str = None,
sender_password: str = None,
use_tls: bool = None,
use_ssl: bool = None,
use_default_config: bool = True
):
"""
初始化邮件发送器
Args:
smtp_server: SMTP服务器地址(如:smtp.163.com, smtp.qq.com),为None时使用默认配置
smtp_port: SMTP端口(25/465/587),为None时使用默认配置
sender_email: 发件人邮箱,为None时使用默认配置
sender_password: 发件人邮箱密码或授权码,为None时使用默认配置
use_tls: 是否使用TLS加密(端口587通常需要),为None时使用默认配置
use_ssl: 是否使用SSL加密(端口465通常需要),为None时使用默认配置
use_default_config: 是否使用默认配置(当参数为None时)
"""
if use_default_config:
default = EmailSender.DEFAULT_CONFIG
self.smtp_server = smtp_server if smtp_server is not None else default['smtp_server']
self.smtp_port = smtp_port if smtp_port is not None else default['smtp_port']
self.sender_email = sender_email if sender_email is not None else default['sender_email']
self.sender_password = sender_password if sender_password is not None else default['sender_password']
self.use_tls = use_tls if use_tls is not None else default['use_tls']
self.use_ssl = use_ssl if use_ssl is not None else default['use_ssl']
else:
self.smtp_server = smtp_server or ''
self.smtp_port = smtp_port or 25
self.sender_email = sender_email or ''
self.sender_password = sender_password or ''
self.use_tls = use_tls if use_tls is not None else False
self.use_ssl = use_ssl if use_ssl is not None else False
def send_email(
self,
receiver_emails: List[str],
subject: str,
content: str,
content_type: str = "plain",
attachments: Optional[List[str]] = None,
cc_emails: Optional[List[str]] = None,
bcc_emails: Optional[List[str]] = None
) -> Dict[str, Any]:
"""
发送邮件
Args:
receiver_emails: 收件人邮箱列表
subject: 邮件主题
content: 邮件内容
content_type: 内容类型,'plain' 或 'html'
attachments: 附件文件路径列表
cc_emails: 抄送邮箱列表
bcc_emails: 密送邮箱列表
Returns:
包含发送结果的字典
"""
try:
# 创建邮件对象
message = MIMEMultipart()
message['From'] = Header(self.sender_email, 'utf-8')
message['To'] = Header(','.join(receiver_emails), 'utf-8')
message['Subject'] = Header(subject, 'utf-8')
# 添加抄送
if cc_emails:
message['Cc'] = Header(','.join(cc_emails), 'utf-8')
# 添加邮件正文
message.attach(MIMEText(content, content_type, 'utf-8'))
# 添加附件
if attachments:
for file_path in attachments:
if os.path.isfile(file_path):
with open(file_path, 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename="{os.path.basename(file_path)}"'
)
message.attach(part)
# 所有收件人(包括抄送和密送)
all_recipients = receiver_emails.copy()
if cc_emails:
all_recipients.extend(cc_emails)
if bcc_emails:
all_recipients.extend(bcc_emails)
# 连接SMTP服务器并发送
if self.use_ssl:
server = smtplib.SMTP_SSL(self.smtp_server, self.smtp_port)
else:
server = smtplib.SMTP(self.smtp_server, self.smtp_port)
if self.use_tls:
server.starttls()
# 登录
server.login(self.sender_email, self.sender_password)
# 发送邮件
server.sendmail(
self.sender_email,
all_recipients,
message.as_string()
)
server.quit()
return {
'success': True,
'message': '邮件发送成功',
'recipients': all_recipients
}
except smtplib.SMTPAuthenticationError as e:
return {
'success': False,
'message': f'SMTP认证失败: {str(e)}',
'error': 'authentication_error'
}
except smtplib.SMTPException as e:
return {
'success': False,
'message': f'SMTP错误: {str(e)}',
'error': 'smtp_error'
}
except Exception as e:
return {
'success': False,
'message': f'发送邮件时发生错误: {str(e)}',
'error': 'unknown_error'
}
def send_simple_email(
self,
receiver_email: str,
subject: str,
content: str,
content_type: str = "plain"
) -> Dict[str, Any]:
"""
发送简单邮件(单个收件人,无附件)
Args:
receiver_email: 收件人邮箱
subject: 邮件主题
content: 邮件内容
content_type: 内容类型,'plain' 或 'html'
Returns:
包含发送结果的字典
"""
return self.send_email(
receiver_emails=[receiver_email],
subject=subject,
content=content,
content_type=content_type
)
# 常用邮箱SMTP配置
SMTP_CONFIGS = {
'163': {
'server': 'smtp.163.com',
'port': 25,
'use_tls': False,
'use_ssl': False
},
'163_ssl': {
'server': 'smtp.163.com',
'port': 465,
'use_tls': False,
'use_ssl': True
},
'qq': {
'server': 'smtp.qq.com',
'port': 587,
'use_tls': True,
'use_ssl': False
},
'qq_ssl': {
'server': 'smtp.qq.com',
'port': 465,
'use_tls': False,
'use_ssl': True
},
'gmail': {
'server': 'smtp.gmail.com',
'port': 587,
'use_tls': True,
'use_ssl': False
},
'outlook': {
'server': 'smtp-mail.outlook.com',
'port': 587,
'use_tls': True,
'use_ssl': False
}
}
if __name__ == '__main__':
# 示例用法
print("=== SMTP邮件发送示例 ===\n")
# 使用163邮箱示例
sender = EmailSender(use_default_config=True)
# 发送简单文本邮件
result = sender.send_simple_email(
receiver_email='112223366544@qq.com', # 替换为收件人邮箱
subject='测试邮件',
content='这是一封测试邮件,使用SMTP方式发送。'
)
print(f"发送结果: {result}")
# 发送HTML邮件示例
html_content = """
<html>
<body>
<h2>这是一封HTML格式的邮件</h2>
<p>邮件内容支持<strong>HTML格式</strong>。</p>
<p style="color: blue;">可以设置样式和颜色。</p>
</body>
</html>
"""
result2 = sender.send_email(
receiver_emails=['123456789@qq.com'],
subject='HTML格式测试邮件',
content=html_content,
content_type='html'
)
print(f"\nHTML邮件发送结果: {result2}")