```html

Building a Local SMS Aggregator: Syncing Samsung Messages Without Cloud Dependencies

Over the past development session, I built a lightweight SMS aggregation system that pulls message history from Samsung devices and generates digest emails—all without relying on Twilio or external SMS APIs. This post walks through the architecture, implementation details, and the operational patterns that make this approach viable for small teams managing multiple phone lines.

The Problem Statement

The existing SMS infrastructure relied on Twilio credentials to ingest and query messages. However, those credentials weren't consistently available in the environment configuration, and the operational overhead of maintaining Twilio integration for historical message retrieval seemed disproportionate. The team already had SMS data exported locally from Samsung devices—the missing piece was a systematic way to parse, aggregate, and surface actionable summaries.

Architecture Overview

The solution consists of three layers:

  • Data Source: Local SMS export files in standardized format
  • Processing Engine: Python script that parses conversations, filters by date/sender, and generates digests
  • Delivery Mechanism: SES-backed email distribution with LaunchAgent scheduling on macOS

This design trades real-time cloud connectivity for operational simplicity and zero external dependencies during the digest generation phase.

File Structure and Implementation

Two primary files were created:

1. SMS Sync Script

/Users/cb/Documents/repos/tools/samsung_sms_sync.py

This Python script handles:

  • Reading the SMS export file (checked at `/path/to/sms_export.txt` or similar standardized location)
  • Parsing conversation headers and message timestamps
  • Filtering messages by date range (e.g., April 25–29 or custom date spans)
  • Extracting specific conversations by phone number using regex matching
  • Compiling digest content with conversation context
  • Invoking AWS SES to send formatted emails

Key functions in the script:

  • parse_sms_export() — Opens the export file and yields conversation objects with headers and message lists
  • filter_by_date_range(conversations, start_date, end_date) — Returns only conversations with activity in the specified window
  • extract_conversation_by_number(conversations, phone_number) — Isolates a single conversation thread and returns all messages
  • compile_digest(conversation_data, recipient_email) — Formats the conversation into readable HTML/plain-text for email delivery
  • send_via_ses(message_body, recipient, subject) — Calls boto3 SES client to deliver the digest

2. LaunchAgent Configuration

/Users/cb/Library/LaunchAgents/com.cb.samsung-sms-sync.plist

This macOS property list file defines a scheduled daemon that runs the SMS sync script at regular intervals. Key configuration details:

  • Label: com.cb.samsung-sms-sync — Unique identifier for the agent
  • ProgramArguments: Full path to Python interpreter and the script
  • StartInterval: Configured for periodic execution (e.g., every 3600 seconds = hourly)
  • StandardOutPath / StandardErrorPath: Logs written to ~/Library/Logs/samsung-sms-sync/ for debugging
  • EnvironmentVariables: AWS credentials injected via environment (sourced from ~/.secrets/repos.env at daemon startup)

The plist is loaded with:

launchctl load ~/Library/LaunchAgents/com.cb.samsung-sms-sync.plist

And can be unloaded or reloaded during development/updates without restarting the system.

SMS Data Format and Parsing Strategy

The Samsung SMS export follows a predictable text-based format:

  • Each conversation begins with a header line containing the phone number and participant name
  • Messages are timestamped and indented or marked with directional indicators (→ for outgoing, ← for incoming)
  • Multiline messages are preserved with consistent formatting

The parsing logic handles:

  • Header detection: Regex pattern to identify conversation boundaries and extract the phone number
  • Timestamp parsing: Converts Samsung's timestamp format to Python datetime objects for range filtering
  • Message direction: Identifies whether each message was sent or received
  • Conversation chunking: Groups all messages between headers into a single conversation object

This approach avoids external SMS libraries and keeps dependencies minimal—only boto3 for SES integration.

AWS Integration: SES Email Delivery

The script uses AWS SES (Simple Email Service) to distribute digests. Configuration details:

  • AWS Region: Configured in ~/.secrets/repos.env as AWS_REGION (e.g., us-west-2)
  • Credentials: AWS access key and secret key stored in ~/.secrets/repos.env; never hardcoded in the script
  • From Address: Verified SES sender identity (e.g., operations@sailjada.com)
  • To Address: Dynamically specified per digest run (e.g., c.b.ladd@gmail.com)

The boto3 SES client call:

client = boto3.client('ses', region_name=os.environ['AWS_REGION'])
response = client.send_email(
    Source='operations@sailjada.com',
    Destination={'ToAddresses': [recipient]},
    Message={
        'Subject': {'Data': subject},
        'Body': {'Html': {'Data': html_body}, 'Text': {'Data': text_body}}
    }
)

Key Design Decisions

Why not Twilio? Twilio credentials weren't reliably available in the development environment. For historical message retrieval (the primary use case), local SMS exports are fresher and require no additional API calls. The tradeoff is that real-time SMS reception would require a separate system—acceptable for a digest-only workflow.

Why LaunchAgent instead of cron? LaunchAgent integrates cleanly with macOS system services, provides built-in log capture, and can be managed with standard launchctl commands. It also respects user login state and system sleep patterns more gracefully than cron.

Why SES instead of local SMTP? SES is stateless, requires no local mail server setup, and integrates with existing AWS infrastructure. Delivery reports are automatic, and SPF/DKIM/DMARC validation is handled by AWS.

Why text-based parsing