qq邮箱操作
// 邮箱配置信息
// 注意:使用QQ邮箱需要先开启IMAP服务并获取授权码
// 设置方法:登录QQ邮箱 -> 设置 -> 账户 -> 开启IMAP/SMTP服务 -> 生成授权码
服务器操作
fetchmail 同步QQ邮箱
nginx搭建web显示本地同步过来的邮箱
ssh端浏览邮箱
通过python脚本实现其他用户登录可浏览邮件
腾讯云
dns转给cloudflare
cloudflare
信件全局转发到QQ
AAAA解析到物理机IPV6
演示站点 fengche.site
博客 xoxome.online
下面是ssh端服务器脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import email
import email.header
import email.utils
import datetime
import subprocess
import tempfile
# 邮件目录
MAIL_DIR = "/home/y/Maildir/INBOX/new/new"
def clear_screen():
"""清屏"""
os.system("clear")
def decode_header(header):
"""解码邮件头部信息"""
if not header:
return "未知"
decoded_header = email.header.decode_header(header)
result = ""
for text, charset in decoded_header:
if isinstance(text, bytes):
try:
if charset:
result += text.decode(charset)
else:
result += text.decode("utf-8", "replace")
except:
result += text.decode("utf-8", "replace")
else:
result += text
return result
def get_email_info(mail_file):
"""获取邮件信息"""
with open(mail_file, "rb") as f:
msg = email.message_from_binary_file(f)
from_addr = decode_header(msg.get("From", "未知"))
to_addr = decode_header(msg.get("To", "未知"))
subject = decode_header(msg.get("Subject", "未知"))
date_str = msg.get("Date", "")
try:
date = email.utils.parsedate_to_datetime(date_str)
date_formatted = date.strftime("%Y-%m-%d %H:%M:%S")
except:
date_formatted = date_str
return {
"from": from_addr,
"to": to_addr,
"subject": subject,
"date": date_formatted
}
def html_to_text(html_content):
"""将HTML转换为可读文本"""
# 使用临时文件保存HTML内容
with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as f:
f.write(html_content.encode('utf-8'))
temp_filename = f.name
try:
# 尝试使用w3m将HTML转换为文本
try:
result = subprocess.run(['w3m', '-dump', temp_filename],
capture_output=True, text=True, check=True)
text = result.stdout
except (subprocess.SubprocessError, FileNotFoundError):
# 如果w3m不可用,尝试使用lynx
try:
result = subprocess.run(['lynx', '-dump', '-force_html', temp_filename],
capture_output=True, text=True, check=True)
text = result.stdout
except (subprocess.SubprocessError, FileNotFoundError):
# 如果lynx也不可用,使用简单的HTML标签移除
text = html_content
# 移除常见HTML标签
tags_to_remove = ['<html>', '</html>', '<body>', '</body>', '<head>', '</head>',
'<script>', '</script>', '<style>', '</style>']
for tag in tags_to_remove:
text = text.replace(tag, '')
# 将<br>和<p>替换为换行符
text = text.replace('<br>', '\n').replace('<p>', '\n').replace('</p>', '\n')
# 移除其他HTML标签
in_tag = False
result = ""
for char in text:
if char == '<':
in_tag = True
elif char == '>':
in_tag = False
elif not in_tag:
result += char
text = result
return text
finally:
# 清理临时文件
try:
os.unlink(temp_filename)
except:
pass
def extract_email_content(mail_file):
"""提取邮件内容"""
with open(mail_file, "rb") as f:
msg = email.message_from_binary_file(f)
content = ""
html_content = ""
if msg.is_multipart():
for part in msg.walk():
content_type = part.get_content_type()
content_disposition = part.get("Content-Disposition", "")
# 忽略附件
if "attachment" in content_disposition:
continue
# 获取文本内容
if content_type == "text/plain" and not content:
payload = part.get_payload(decode=True)
if payload:
charset = part.get_content_charset()
try:
if charset:
content += payload.decode(charset)
else:
content += payload.decode("utf-8", "replace")
except:
content += payload.decode("utf-8", "replace")
# 获取HTML内容
elif content_type == "text/html" and not html_content:
payload = part.get_payload(decode=True)
if payload:
charset = part.get_content_charset()
try:
if charset:
html_content += payload.decode(charset)
else:
html_content += payload.decode("utf-8", "replace")
except:
html_content += payload.decode("utf-8", "replace")
else:
# 非多部分邮件
content_type = msg.get_content_type()
payload = msg.get_payload(decode=True)
if payload:
charset = msg.get_content_charset()
try:
decoded = payload.decode(charset if charset else "utf-8", "replace")
if content_type == "text/plain":
content = decoded
elif content_type == "text/html":
html_content = decoded
except:
content = payload.decode("utf-8", "replace")
# 如果有HTML内容但没有纯文本内容,转换HTML为文本
if html_content and not content:
content = html_to_text(html_content)
# 如果没有任何内容
if not content and not html_content:
content = "【无法解析的邮件内容】"
return content, html_content
def list_emails():
"""列出邮件"""
clear_screen()
print("欢迎使用邮件查看系统")
print("=======================")
print()
if not os.path.isdir(MAIL_DIR):
print("邮件目录不存在: " + MAIL_DIR)
input("按Enter键退出...")
sys.exit(1)
mail_files = []
try:
# 获取所有邮件文件,按修改时间排序
mail_files = sorted([f for f in os.listdir(MAIL_DIR) if os.path.isfile(os.path.join(MAIL_DIR, f))],
key=lambda x: os.path.getmtime(os.path.join(MAIL_DIR, x)),
reverse=True)
except Exception as e:
print("读取邮件目录出错: " + str(e))
input("按Enter键退出...")
sys.exit(1)
if not mail_files:
print("没有新邮件")
print("按Enter键同步邮件,按q退出:")
choice = input().strip()
if choice.lower() != "q":
sync_mail()
return list_emails() # 重新加载邮件列表
else:
sys.exit(0)
print("找到 " + str(len(mail_files)) + " 封新邮件:")
print()
# 显示最多5封邮件
displayed_files = []
for i, mail_file in enumerate(mail_files[:5]):
full_path = os.path.join(MAIL_DIR, mail_file)
try:
info = get_email_info(full_path)
displayed_files.append(mail_file)
print(str(i+1) + ") " + mail_file)
print(" 从: " + info["from"])
print(" 主题: " + info["subject"])
print(" 日期: " + info["date"])
print()
except Exception as e:
print("读取邮件 " + mail_file + " 出错: " + str(e))
print()
return displayed_files
def sync_mail():
"""同步邮件"""
clear_screen()
print("正在使用y用户权限同步邮件...")
try:
# 使用sudo以y用户身份运行fetchmail
result = subprocess.run(['sudo', '-u', 'y', 'fetchmail', '-v'], capture_output=True, text=True)
output = result.stdout
error = result.stderr
if output:
print(output)
if error:
print()
print()
# 检查是否成功同步了新邮件
if "reading message" in (output or "") or "messages" in (output or ""):
print("成功同步了新邮件!")
else:
print("没有新邮件或同步失败。")
except Exception as e:
print("同步邮件出错: " + str(e))
print()
input("按Enter键继续...")
def view_email(mail_file):
"""查看邮件内容"""
clear_screen()
full_path = os.path.join(MAIL_DIR, mail_file)
try:
# 获取邮件信息
info = get_email_info(full_path)
print("邮件: " + mail_file)
print("=======================")
print("从: " + info["from"])
print("收件人: " + info["to"])
print("主题: " + info["subject"])
print("日期: " + info["date"])
print("=======================")
print()
# 提取邮件内容
text_content, html_content = extract_email_content(full_path)
if html_content:
print("邮件内容 (转换自HTML):")
print("=======================")
print(text_content)
print("=======================")
print()
print("选项: 1) 返回邮件列表 2) 查看原始HTML 3) 同步邮件 [1-3]:")
view_choice = input().strip()
if view_choice == "2":
# 使用临时文件显示HTML
with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as f:
f.write(html_content.encode('utf-8'))
temp_filename = f.name
try:
# 尝试使用不同的HTML查看器
browsers = [
['w3m', temp_filename],
['lynx', temp_filename],
['less', temp_filename]
]
for browser in browsers:
try:
subprocess.run(browser)
break
except (subprocess.SubprocessError, FileNotFoundError):
continue
finally:
os.unlink(temp_filename)
elif view_choice == "3":
sync_mail()
else:
print("邮件内容:")
print("=======================")
print(text_content)
print("=======================")
print()
print("选项: 1) 返回邮件列表 2) 同步邮件 [1-2]:")
view_choice = input().strip()
if view_choice == "2":
sync_mail()
return True
except Exception as e:
print("查看邮件出错: " + str(e))
print()
input("按Enter键返回...")
return False
def main():
"""主函数"""
while True:
displayed_files = list_emails()
print("输入邮件编号查看内容,按Enter查看最新邮件,按s同步邮件,按q退出:")
choice = input().strip()
if choice.lower() == "q":
print("谢谢使用,再见!")
break
elif choice.lower() == "s":
sync_mail()
continue
mail_to_view = None
if not choice or choice == "1":
# 查看最新邮件
if displayed_files:
mail_to_view = displayed_files[0]
elif choice.isdigit():
# 查看选定邮件
idx = int(choice) - 1
if 0 <= idx < len(displayed_files):
mail_to_view = displayed_files[idx]
else:
print("无效选择!")
input("按Enter键继续...")
continue
else:
print("无效选择!")
input("按Enter键继续...")
continue
if mail_to_view:
view_email(mail_to_view)
if __name__ == "__main__":
# 检查并安装必要的HTML渲染工具
try:
for pkg in ['w3m', 'lynx']:
try:
subprocess.run(['which', pkg], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
break
except:
pass
except:
pass
try:
main()
except KeyboardInterrupt:
print("\n程序已退出")
sys.exit(0)
except Exception as e:
print("程序发生错误: " + str(e))
input("按Enter键退出...")
sys.exit(1)