This commit is contained in:
2026-01-27 16:02:24 +03:00
parent ec9c4dc55f
commit 35e7ab6066

114
main.py
View File

@@ -1,6 +1,9 @@
import os
import logging
import subprocess
import email
from email import policy
from email.parser import BytesParser
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
@@ -32,7 +35,8 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
'Available commands:\n'
'/start - Start the bot\n'
'/help - Show help message\n'
'/checkmail - Check new emails'
'/checkmail - List new emails\n'
'/readmail <number> - Read a specific email'
)
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
@@ -41,7 +45,8 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
'Available commands:\n'
'/start - Start the bot\n'
'/help - Show this help message\n'
'/checkmail - Check new emails in your mailbox\n'
'/checkmail - List new emails in your mailbox\n'
'/readmail <number> - Read a specific email (e.g., /readmail 1)\n'
'\nJust send me any text and I will echo it back!'
)
@@ -75,15 +80,19 @@ async def check_mail(update: Update, context: ContextTypes.DEFAULT_TYPE):
if not emails or emails == ['']:
await update.message.reply_text("📭 No new emails!")
# Clear stored emails
context.user_data['emails'] = []
return
# Store email list in user context for readmail command
context.user_data['emails'] = emails
# Format the response
response = f"📬 You have {len(emails)} new email(s):\n\n"
for i, email in enumerate(emails, 1):
for i, email_file in enumerate(emails, 1):
# Parse email filename to extract info
# Format: timestamp.M###P###.mail,S=size,W=size
parts = email.split(',')
timestamp_part = parts[0] if parts else email
parts = email_file.split(',')
timestamp_part = parts[0] if parts else email_file
size_part = parts[1] if len(parts) > 1 else ""
# Extract size in bytes
@@ -100,6 +109,7 @@ async def check_mail(update: Update, context: ContextTypes.DEFAULT_TYPE):
response += f"{i}. {timestamp_part} ({size_str})\n"
response += f"\n💡 Use /readmail <number> to read an email"
await update.message.reply_text(response)
except subprocess.TimeoutExpired:
@@ -110,6 +120,97 @@ async def check_mail(update: Update, context: ContextTypes.DEFAULT_TYPE):
logger.error(f"Error checking mail: {e}")
await update.message.reply_text(f"❌ An error occurred: {str(e)}")
async def read_mail(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Read a specific email by number."""
# Check authorization
if not check_authorization(update):
await update.message.reply_text("❌ You are not authorized to use this command.")
logger.warning(f"Unauthorized access attempt by user {update.effective_user.id}")
return
# Check if user has checked mail first
if 'emails' not in context.user_data or not context.user_data['emails']:
await update.message.reply_text("📭 Please use /checkmail first to see available emails.")
return
# Parse email number from command
if not context.args:
await update.message.reply_text("❌ Please specify an email number. Usage: /readmail 1")
return
try:
email_num = int(context.args[0])
emails = context.user_data['emails']
if email_num < 1 or email_num > len(emails):
await update.message.reply_text(f"❌ Invalid email number. Please choose between 1 and {len(emails)}")
return
# Get the email filename (array is 0-indexed)
email_filename = emails[email_num - 1]
email_path = f"/var/mail/fymio.us/me/new/{email_filename}"
# Read email content using docker exec
result = subprocess.run(
['docker', 'exec', 'mailserver', 'cat', email_path],
capture_output=True,
timeout=10
)
if result.returncode != 0:
await update.message.reply_text(f"❌ Error reading email:\n`{result.stderr.decode()}`", parse_mode='Markdown')
return
# Parse email
msg = BytesParser(policy=policy.default).parsebytes(result.stdout)
# Extract email details
subject = msg.get('Subject', 'No Subject')
from_addr = msg.get('From', 'Unknown')
to_addr = msg.get('To', 'Unknown')
date = msg.get('Date', 'Unknown')
# Get email body
body = ""
if msg.is_multipart():
for part in msg.walk():
content_type = part.get_content_type()
if content_type == "text/plain":
try:
body = part.get_content()
break
except:
pass
else:
try:
body = msg.get_content()
except:
body = "Could not extract email body"
# Truncate body if too long (Telegram has message length limits)
max_body_length = 3000
if len(body) > max_body_length:
body = body[:max_body_length] + "\n\n... (truncated)"
# Format response
response = f"📧 **Email #{email_num}**\n\n"
response += f"**From:** {from_addr}\n"
response += f"**To:** {to_addr}\n"
response += f"**Date:** {date}\n"
response += f"**Subject:** {subject}\n"
response += f"\n━━━━━━━━━━━━━━━━━━\n\n"
response += body
await update.message.reply_text(response, parse_mode='Markdown')
except ValueError:
await update.message.reply_text("❌ Invalid email number. Please provide a number.")
except subprocess.TimeoutExpired:
await update.message.reply_text("❌ Command timed out while reading email.")
except Exception as e:
logger.error(f"Error reading mail: {e}")
await update.message.reply_text(f"❌ An error occurred: {str(e)}")
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Echo the user message."""
await update.message.reply_text(f"You said: {update.message.text}")
@@ -127,6 +228,7 @@ def main():
application.add_handler(CommandHandler("start", start))
application.add_handler(CommandHandler("help", help_command))
application.add_handler(CommandHandler("checkmail", check_mail))
application.add_handler(CommandHandler("readmail", read_mail))
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
# Start the Bot