家庭服务器IPV6搭建无限邮箱系统指南

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)
相关推荐
星释1 分钟前
阿里云服务迁移实战: 06-切换DNS
运维·阿里云·云计算
x-cmd24 分钟前
[250430] Kali Linux 存储库密钥丢失导致所有用户无法正常更新 APT
linux·运维·服务器·kali linux
灏瀚星空27 分钟前
量化交易之数学与统计学基础2.3——线性代数与矩阵运算 | 线性方程组
笔记·python·信息可视化·数据挖掘·回归·开源·最小二乘法
素雪风华29 分钟前
conda管理python环境
python·conda·pip
Theodore_102241 分钟前
Python3(19)数据结构
大数据·开发语言·数据结构·python·网络爬虫
星雨流星天的笔记本1 小时前
3、Linux操作系统下,linux的技术手册使用(man)
linux·服务器
王大傻09281 小时前
python + segno 生成个人二维码
开发语言·python
alpha xu1 小时前
微软GraphRAG的安装和在RAG中的使用体会
人工智能·python·microsoft·rag·graphrag
一刀到底2111 小时前
从实列中学习linux shell6: 写一个 shell 脚本 过滤 恶意ip 攻击
linux·运维·shell
神奇侠20241 小时前
基于tabula对pdf中多个excel进行识别并转换成word中的优化(五)
python·pdf·tabula