Add email flags and unsubscribe features
All checks were successful
Build And Test / publish (push) Successful in 48s
All checks were successful
Build And Test / publish (push) Successful in 48s
- Add set_email_flags tool for IMAP flags (standard + custom keywords) - Add unsubscribe_email tool with List-Unsubscribe header parsing - Support RFC 8058 one-click unsubscribe - Add UnsubscribeInfo model to email responses - Add data field to OperationResult for extra context - Include test.sh script for MCP server testing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,9 +17,11 @@ from models.email_models import (
|
||||
EmailSummary,
|
||||
Email,
|
||||
EmailList,
|
||||
UnsubscribeInfo,
|
||||
)
|
||||
from models.common import OperationResult
|
||||
from config import Settings
|
||||
from services.unsubscribe_service import parse_unsubscribe_header, execute_unsubscribe
|
||||
|
||||
|
||||
def decode_mime_header(header) -> str:
|
||||
@@ -239,11 +241,14 @@ class EmailService:
|
||||
|
||||
# Get headers
|
||||
headers = {}
|
||||
for key in ["Message-ID", "In-Reply-To", "References", "X-Priority"]:
|
||||
for key in ["Message-ID", "In-Reply-To", "References", "X-Priority", "List-Unsubscribe", "List-Unsubscribe-Post"]:
|
||||
value = msg.get(key)
|
||||
if value:
|
||||
headers[key] = decode_mime_header(value)
|
||||
|
||||
# Parse unsubscribe info
|
||||
unsubscribe_info = parse_unsubscribe_header(msg)
|
||||
|
||||
return Email(
|
||||
id=str(uid),
|
||||
mailbox=mailbox,
|
||||
@@ -262,6 +267,7 @@ class EmailService:
|
||||
headers=headers,
|
||||
in_reply_to=headers.get("In-Reply-To"),
|
||||
references=headers.get("References", "").split() if headers.get("References") else [],
|
||||
unsubscribe=unsubscribe_info if unsubscribe_info.available else None,
|
||||
)
|
||||
|
||||
def search_emails(
|
||||
@@ -568,3 +574,76 @@ class EmailService:
|
||||
if name in trash_names or b"\\Trash" in flags:
|
||||
return name
|
||||
return None
|
||||
|
||||
def set_flags(
|
||||
self,
|
||||
email_id: str,
|
||||
mailbox: str,
|
||||
add_flags: Optional[list[str]] = None,
|
||||
remove_flags: Optional[list[str]] = None,
|
||||
) -> OperationResult:
|
||||
"""
|
||||
Set or remove IMAP flags on an email.
|
||||
|
||||
Standard flags: \\Seen, \\Answered, \\Flagged, \\Deleted, \\Draft
|
||||
Custom keywords are also supported (server-dependent).
|
||||
|
||||
Args:
|
||||
email_id: The unique ID of the email
|
||||
mailbox: The mailbox containing the email
|
||||
add_flags: List of flags to add
|
||||
remove_flags: List of flags to remove
|
||||
"""
|
||||
try:
|
||||
client = self._get_imap_client()
|
||||
client.select_folder(mailbox)
|
||||
uid = int(email_id)
|
||||
|
||||
if add_flags:
|
||||
client.add_flags([uid], add_flags)
|
||||
if remove_flags:
|
||||
client.remove_flags([uid], remove_flags)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
message=f"Flags updated for email {email_id}",
|
||||
id=email_id,
|
||||
data={
|
||||
"added": add_flags or [],
|
||||
"removed": remove_flags or [],
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
return OperationResult(success=False, message=str(e))
|
||||
|
||||
async def unsubscribe(
|
||||
self,
|
||||
mailbox: str,
|
||||
email_id: str,
|
||||
) -> OperationResult:
|
||||
"""
|
||||
Attempt to unsubscribe from a mailing list based on email headers.
|
||||
|
||||
Args:
|
||||
mailbox: The mailbox containing the email
|
||||
email_id: The unique ID of the email
|
||||
|
||||
Returns:
|
||||
OperationResult with unsubscribe status
|
||||
"""
|
||||
# First, read the email to get unsubscribe info
|
||||
email_data = self.read_email(mailbox, email_id)
|
||||
if not email_data:
|
||||
return OperationResult(
|
||||
success=False,
|
||||
message=f"Email {email_id} not found in {mailbox}"
|
||||
)
|
||||
|
||||
if not email_data.unsubscribe:
|
||||
return OperationResult(
|
||||
success=False,
|
||||
message="This email does not have unsubscribe information"
|
||||
)
|
||||
|
||||
# Execute unsubscribe
|
||||
return await execute_unsubscribe(email_data.unsubscribe)
|
||||
|
||||
Reference in New Issue
Block a user