Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Email Reply Parser Laravel Package

willdurand/email-reply-parser

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require willdurand/email-reply-parser
    

    Add to composer.json if using a monorepo or constrained environment.

  2. First Use Case: Parse an incoming email reply to extract the original message and new content. Example:

    use EmailReplyParser\Parser\EmailParser;
    
    $parser = new EmailParser();
    $email = $parser->parse($rawEmailContent);
    
  3. Key Methods to Explore:

    • $email->getFragments() → Array of Fragment objects (original message, new content, signatures).
    • $email->getVisibleText() → Cleaned-up text (ignores quoted replies/signatures).
    • $fragment->isSignature() → Check if a fragment is a signature.

Where to Look First

  • README → Quickstart and API reference.
  • Tests → Real-world examples of parsing edge cases (e.g., nested replies, malformed emails).
  • GitHub Issues → Common pain points (e.g., handling non-standard email formats).

Implementation Patterns

Core Workflows

1. Extracting New Content from Replies

$email = (new EmailParser())->parse($replyEmail);
$newContent = implode("\n", array_filter($email->getFragments(), fn($f) => !$f->isSignature() && !$f->isQuoted()));
  • Use Case: Strip quoted text/signatures to show only the user’s new input (e.g., for a helpdesk ticket system).

2. Thread-Aware Email Processing

$fragments = $email->getFragments();
$originalMessage = end(array_filter($fragments, fn($f) => $f->isQuoted() && !$f->isSignature()));
  • Use Case: Reconstruct the original email thread for context (e.g., Slack/Teams notifications).

3. Signature Detection

$signatures = array_filter($email->getFragments(), fn($f) => $f->isSignature());
  • Use Case: Ignore signatures in analytics or display only the body text.

4. Integration with Laravel Mail

use Illuminate\Support\Facades\Mail;
use EmailReplyParser\Parser\EmailParser;

Mail::raw($rawEmail, function ($message) {
    $parser = new EmailParser();
    $email = $parser->parse($rawEmail);
    $message->subject("Re: " . $email->getVisibleText());
});
  • Use Case: Auto-generate subject lines from parsed email content.

Laravel-Specific Patterns

Service Provider Binding

// app/Providers/AppServiceProvider.php
public function register()
{
    $this->app->singleton(EmailParser::class, fn() => new EmailParser());
}
  • Why: Avoid reinstantiating the parser; useful for dependency injection.

Command for Debugging

// app/Console/Commands/ParseEmailCommand.php
use EmailReplyParser\Parser\EmailParser;

public function handle()
{
    $email = (new EmailParser())->parse($this->argument('content'));
    $this->info("Visible Text:\n" . $email->getVisibleText());
    $this->info("\nFragments:");
    foreach ($email->getFragments() as $fragment) {
        $this->line("- " . ($fragment->isSignature() ? "[SIGNATURE]" : "[CONTENT]"));
    }
}
  • Use Case: CLI tool to inspect email parsing logic during development.

Middleware for Incoming Emails

// app/Http/Middleware/ParseIncomingEmail.php
public function handle($request, Closure $next)
{
    if ($request->is('api/webhooks/email')) {
        $email = (new EmailParser())->parse($request->getContent());
        $request->merge(['parsed_email' => $email]);
    }
    return $next($request);
}
  • Use Case: Pre-process raw email payloads (e.g., from webhooks) into structured data.

Gotchas and Tips

Pitfalls

  1. False Signature Detection

    • Issue: The parser may misclassify long quoted blocks as signatures.
    • Fix: Override detection logic:
      $fragment->isSignature() && $fragment->getContent() !== "Expected signature pattern";
      
    • Tip: Use getVisibleText() as a fallback if fragments are unreliable.
  2. Non-Standard Email Formats

    • Issue: HTML emails or emails with unusual quoting (e.g., > vs >) may break parsing.
    • Fix: Pre-process content with strip_tags() or a regex to normalize formatting:
      $normalizedContent = preg_replace('/<[^>]*>/', '', $rawEmail);
      
  3. Performance with Large Emails

    • Issue: Parsing very long emails (e.g., 10K+ lines) may cause memory issues.
    • Fix: Stream the email content or use chunked parsing (though the library isn’t designed for this).
  4. Locale-Specific Quoting

    • Issue: Some languages use » or other symbols for quoting, which the parser may not handle.
    • Fix: Extend the EmailParser class to support custom quote markers:
      $parser = new EmailParser();
      $parser->setQuoteMarkers(['»', '>']);
      

Debugging Tips

  1. Inspect Fragments Manually

    foreach ($email->getFragments() as $i => $fragment) {
        echo "Fragment $i: " . ($fragment->isSignature() ? "[SIGNATURE]" : "[CONTENT]") . "\n";
        echo $fragment->getContent() . "\n\n";
    }
    
    • Why: Helps identify misclassified fragments or unexpected content.
  2. Test with Real-World Examples

    • Use the test cases as a baseline.
    • Add your own test emails to a tests/emails/ directory and write assertions:
      $this->assertCount(2, $email->getFragments());
      $this->assertTrue($email->getFragments()[0]->isQuoted());
      
  3. Handle Edge Cases

    • Empty Emails: Check empty($email->getFragments()).
    • Malformed Emails: Wrap parsing in a try-catch:
      try {
          $email = (new EmailParser())->parse($rawEmail);
      } catch (\Exception $e) {
          // Fallback: return raw content or log the error
      }
      

Extension Points

  1. Custom Fragment Types

    • Extend the Fragment class to add metadata (e.g., isSpam(), getSentimentScore()).
  2. Post-Processing Hooks

    • Override the EmailParser class to modify fragments after parsing:
      class CustomEmailParser extends EmailParser {
          protected function postProcessFragments(array $fragments): array {
              return array_map(function ($fragment) {
                  if ($fragment->isQuoted()) {
                      $fragment->setContent(str_replace('old-term', 'new-term', $fragment->getContent()));
                  }
                  return $fragment;
              }, $fragments);
          }
      }
      
  3. Integration with Laravel Notifications

    • Use parsed fragments to dynamically generate email notifications:
      $newContent = implode("\n", array_filter($email->getFragments(), fn($f) => !$f->isQuoted()));
      Notification::send($user, new CustomEmailNotification($newContent));
      

Configuration Quirks

  • No Built-in Config: The library is stateless; all logic is in-memory. For shared state (e.g., custom quote markers), use Laravel’s config() or a service container binding.
  • Thread Safety: Safe for concurrent use (no static properties).
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui