ekandreas/filament-resonator

A Filament plugin for managing email inbox with Resend and AI-powered features via Prism

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/ekandreas/filament-resonator

v0.1.0-beta 2026-01-03 14:00 UTC

This package is auto-updated.

Last update: 2026-01-03 14:13:00 UTC


README

A powerful email inbox plugin for Filament 4 with Resend integration and AI-powered features via Prism.

Filament Laravel PHP License

Features

  • ๐Ÿ“ง Full Email Inbox - Send, receive, and manage emails directly in Filament
  • ๐Ÿงต Thread Grouping - Automatic conversation threading via headers
  • ๐Ÿ“ Folder Management - Inbox, Sent, Archive, Spam, Trash + custom folders
  • ๐Ÿค– AI Spam Detection - Automatic spam classification using Prism + LLM
  • ๐Ÿ‘ค Contact Enrichment - Extract contact info from emails via AI
  • ๐Ÿ“ Reply Snippets - Pre-defined templates for quick responses
  • ๐Ÿ”„ Auto Sync - Scheduled syncing via artisan command
  • ๐ŸŒ Multilingual - 9 languages included (EN, SV, DE, FR, ES, NB, FI, DA, PT)

Architecture Overview

graph TB
    subgraph "Filament Panel"
        IR[InboxResource]
        FR[FolderResource]
        SR[SnippetResource]
        SFR[SpamFilterResource]
    end

    subgraph "Core"
        RP[ResonatorPlugin]
        RSP[ResonatorServiceProvider]
    end

    subgraph "Actions"
        SE[SyncEmails]
        SR2[SendReply]
        CO[CleanupOldMessages]
    end

    subgraph "Jobs"
        DS[DetectSpam]
        EC[EnrichContact]
    end

    subgraph "External Services"
        RESEND[(Resend API)]
        PRISM[Prism]
        LLM[(OpenAI / Anthropic / Ollama)]
    end

    RP --> IR
    RP --> FR
    RP --> SR
    RP --> SFR

    IR --> SE
    IR --> SR2
    SE --> RESEND
    SR2 --> RESEND
    CO --> RESEND

    DS --> PRISM
    EC --> PRISM
    PRISM --> LLM
Loading

Technology Stack

graph LR
    subgraph "Frontend"
        F[Filament 4]
        B[Blade Views]
        L[Livewire]
    end

    subgraph "Backend"
        LA[Laravel 12]
        SP[Spatie Package Tools]
    end

    subgraph "Email"
        RE[Resend API]
        SA[Saloon HTTP]
    end

    subgraph "AI Layer"
        PR[Prism PHP]
        OA[OpenAI]
        AN[Anthropic]
        OL[Ollama]
    end

    F --> LA
    B --> F
    L --> F
    LA --> SP
    LA --> SA
    SA --> RE
    LA --> PR
    PR --> OA
    PR --> AN
    PR --> OL
Loading

Data Model

erDiagram
    ResonatorFolder ||--o{ ResonatorThread : contains
    ResonatorThread ||--o{ ResonatorEmail : contains
    ResonatorThread }o--o{ ResonatorContact : has
    ResonatorEmail ||--o{ ResonatorAttachment : has
    User ||--o{ ResonatorThread : handles
    User ||--o{ ResonatorSpamFilter : creates

    ResonatorFolder {
        bigint id PK
        string name
        string slug UK
        string icon
        string color
        boolean is_system
        integer sort_order
    }

    ResonatorThread {
        bigint id PK
        bigint folder_id FK
        string subject
        string participant_email
        string participant_name
        boolean is_starred
        boolean is_read
        datetime last_message_at
        bigint handled_by FK
        datetime handled_at
    }

    ResonatorEmail {
        bigint id PK
        bigint thread_id FK
        string resend_id UK
        string message_id
        string in_reply_to
        boolean is_inbound
        string from_email
        string from_name
        json to
        string subject
        longtext html
        longtext text
        datetime sent_at
    }

    ResonatorAttachment {
        bigint id PK
        bigint email_id FK
        string resend_id
        string filename
        string content_type
        bigint size
    }

    ResonatorContact {
        bigint id PK
        string email UK
        string name
        string phone
        string company
        string unsubscribe_token UK
        datetime unsubscribed_at
    }

    ResonatorSnippet {
        bigint id PK
        string name
        string shortcut UK
        string subject
        longtext body
        integer sort_order
        boolean is_active
    }

    ResonatorSpamFilter {
        bigint id PK
        string email UK
        text reason
        bigint created_by FK
    }
Loading

Email Sync Flow

sequenceDiagram
    participant Scheduler
    participant SyncEmails
    participant Resend
    participant Database
    participant DetectSpam
    participant Prism
    participant LLM

    Scheduler->>SyncEmails: resonator:sync
    SyncEmails->>Resend: GET /emails/receiving
    Resend-->>SyncEmails: List of emails

    loop For each email
        SyncEmails->>Resend: GET /emails/receiving/{id}
        Resend-->>SyncEmails: Email details

        SyncEmails->>SyncEmails: Check spam filter
        SyncEmails->>SyncEmails: Find/create thread
        SyncEmails->>Database: Create ResonatorEmail
        SyncEmails->>Database: Create ResonatorAttachment(s)

        SyncEmails->>DetectSpam: Dispatch job (5s delay)
    end

    DetectSpam->>Prism: Structured prompt
    Prism->>LLM: Analyze email content
    LLM-->>Prism: JSON response
    Prism-->>DetectSpam: {is_spam, reason}

    alt is_spam = true
        DetectSpam->>Database: Move thread to spam folder
    end

    SyncEmails-->>Scheduler: {synced, skipped, spam, errors}
Loading

Reply Flow

sequenceDiagram
    participant User
    participant ViewThread
    participant SendReply
    participant Mail
    participant Resend
    participant Database

    User->>ViewThread: Click Reply
    User->>ViewThread: Select snippet (optional)
    User->>ViewThread: Write message
    User->>ViewThread: Submit

    ViewThread->>SendReply: execute(thread, body)
    SendReply->>SendReply: Build signature
    SendReply->>SendReply: Set In-Reply-To headers
    SendReply->>Mail: Send via Laravel Mail
    Mail->>Resend: POST /emails
    Resend-->>Mail: {id, status}

    SendReply->>Database: Create ResonatorEmail (is_inbound=false)
    SendReply->>Database: Move thread to Sent folder
    SendReply->>Database: Mark as handled

    SendReply-->>ViewThread: Success
    ViewThread-->>User: Notification + Redirect
Loading

Installation

Step 1: Install via Composer

composer require ekandreas/filament-resonator

Step 2: Publish and run migrations

php artisan vendor:publish --tag="resonator-migrations"
php artisan migrate

Step 3: Publish config (optional)

php artisan vendor:publish --tag="resonator-config"

Step 4: Configure Prism

Resonator uses Prism for AI features. Configure your preferred provider in config/prism.php:

// config/prism.php
return [
    'providers' => [
        'openai' => [
            'api_key' => env('OPENAI_API_KEY'),
        ],
        // Or use Anthropic, Ollama, etc.
    ],
];

Step 5: Register the plugin

In your Filament Panel Provider:

use EkAndreas\Resonator\ResonatorPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            ResonatorPlugin::make(),
        ]);
}

Configuration

Environment Variables

# Required - Resend
RESEND_KEY=re_xxxxxxxxxxxx

# Required for AI features - Choose one provider
OPENAI_API_KEY=sk-xxxxxxxxxxxx
# or
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxx

# Optional - Mail settings
RESONATOR_FROM_ADDRESS=hello@example.com
RESONATOR_FROM_NAME="My App"

# Optional - AI settings
RESONATOR_AI_ENABLED=true
RESONATOR_AI_PROVIDER=openai
RESONATOR_AI_MODEL=gpt-4o-mini

# Optional - Features
RESONATOR_SPAM_DETECTION=true
RESONATOR_CONTACT_ENRICHMENT=true
RESONATOR_CLEANUP_ENABLED=true
RESONATOR_CLEANUP_DAYS=30

Config File

// config/resonator.php

return [
    'resend' => [
        'key' => env('RESEND_KEY'),
    ],

    'mail' => [
        'from_address' => env('RESONATOR_FROM_ADDRESS'),
        'from_name' => env('RESONATOR_FROM_NAME'),
    ],

    'ai' => [
        'enabled' => env('RESONATOR_AI_ENABLED', true),
        'provider' => env('RESONATOR_AI_PROVIDER', 'openai'),
        'model' => env('RESONATOR_AI_MODEL', 'gpt-4o-mini'),
    ],

    'spam_detection' => [
        'enabled' => env('RESONATOR_SPAM_DETECTION', true),
        'delay_seconds' => 5,
    ],

    'contact_enrichment' => [
        'enabled' => env('RESONATOR_CONTACT_ENRICHMENT', true),
        'max_emails_to_analyze' => 3,
        'max_text_length' => 3000,
    ],

    'navigation' => [
        'group' => 'Resonator',
        'sort' => 100,
    ],

    'pagination' => [
        'default' => 25,
        'options' => [25, 50, 100],
    ],
];

Prism AI Integration

Resonator leverages Prism's structured output feature for reliable AI responses:

graph TD
    subgraph "Prism Structured Output"
        A[Email Content] --> B[System Prompt]
        B --> C[ObjectSchema]
        C --> D{Provider}
        D --> E[OpenAI]
        D --> F[Anthropic]
        D --> G[Ollama]
        E --> H[JSON Response]
        F --> H
        G --> H
        H --> I[Validated Data]
    end
Loading

Spam Detection Schema

$schema = new ObjectSchema(
    name: 'spam_detection',
    properties: [
        new BooleanSchema('is_spam'),
        new StringSchema('reason'),
    ],
    requiredFields: ['is_spam']
);

Contact Enrichment Schema

$schema = new ObjectSchema(
    name: 'contact_info',
    properties: [
        new StringSchema('name'),
        new StringSchema('phone'),
        new StringSchema('company'),
    ]
);

Supported Prism Providers

Provider Model Examples Config
OpenAI gpt-4o-mini, gpt-4o OPENAI_API_KEY
Anthropic claude-3-haiku, claude-3-sonnet ANTHROPIC_API_KEY
Ollama llama3, mistral Local installation

Plugin Options

ResonatorPlugin::make()
    ->folders(true)      // Enable folder management
    ->snippets(true)     // Enable reply snippets
    ->spamFilters(true)  // Enable spam filter management

Scheduled Syncing

Add to your routes/console.php or scheduler:

use Illuminate\Support\Facades\Schedule;

Schedule::command('resonator:sync')->everyFiveMinutes();

Or in app/Console/Kernel.php:

protected function schedule(Schedule $schedule): void
{
    $schedule->command('resonator:sync')->everyFiveMinutes();
}

Command Reference

# Sync emails and cleanup old messages
php artisan resonator:sync

# Sync without cleanup
php artisan resonator:sync --no-cleanup

# Custom cleanup period
php artisan resonator:sync --cleanup-days=60

Navigation Structure

graph LR
    subgraph "Resonator Menu Group"
        A[๐Ÿ“ฅ Inbox] --> A1[View Threads]
        A --> A2[Compose]
        A --> A3[Sync]

        B[๐Ÿ“ Folders] --> B1[Manage Folders]

        C[๐Ÿ“ Snippets] --> C1[Manage Templates]

        D[๐Ÿ›ก๏ธ Spam Filters] --> D1[Manage Blocked]
    end
Loading

System Folders

The following folders are created automatically:

Folder Slug Icon Purpose
Inbox inbox ๐Ÿ“ฅ Incoming messages
Sent sent ๐Ÿ“ค Outgoing messages
Archive archive ๐Ÿ“ฆ Archived threads
Spam spam ๐Ÿ›ก๏ธ Spam messages
Trash trash ๐Ÿ—‘๏ธ Deleted messages

AI Features

Spam Detection Flow

flowchart TD
    A[New Email Received] --> B{Is Inbound?}
    B -->|No| END[Skip]
    B -->|Yes| C[Queue DetectSpam Job]
    C --> D[Wait 5 seconds]
    D --> E[Build Prompt]
    E --> F[Prism Structured Call]
    F --> G[LLM Analysis]
    G --> H{AI Response}
    H -->|is_spam: true| I[Move to Spam Folder]
    H -->|is_spam: false| J[Keep in Inbox]
    I --> K[Log Result]
    J --> K
Loading

Contact Enrichment

flowchart TD
    A[Thread Created] --> B[Queue EnrichContact Job]
    B --> C{Contact Complete?}
    C -->|Yes| END[Skip]
    C -->|No| D[Collect Last 3 Emails]
    D --> E[Limit Text to 3000 chars]
    E --> F[Prism Structured Call]
    F --> G[LLM Extraction]
    G --> H[Extract: name, phone, company]
    H --> I{Found New Data?}
    I -->|Yes| J[Update Contact]
    I -->|No| END2[Skip]
Loading

Thread Matching Logic

flowchart TD
    A[New Email] --> B{Has In-Reply-To?}
    B -->|Yes| C[Find by Message-ID]
    C --> D{Found?}
    D -->|Yes| E[Add to Existing Thread]
    D -->|No| F{Match Subject + Sender?}
    B -->|No| F
    F -->|Yes| G[Within 30 days?]
    G -->|Yes| E
    G -->|No| H[Create New Thread]
    F -->|No| H
Loading

Translations

Resonator includes translations for the following languages:

Language Code File
English en resources/lang/en/resonator.php
Swedish sv resources/lang/sv/resonator.php
German de resources/lang/de/resonator.php
French fr resources/lang/fr/resonator.php
Spanish es resources/lang/es/resonator.php
Norwegian nb resources/lang/nb/resonator.php
Finnish fi resources/lang/fi/resonator.php
Danish da resources/lang/da/resonator.php
Portuguese pt resources/lang/pt/resonator.php

To add more languages:

php artisan vendor:publish --tag="resonator-translations"

Then create your translation file in lang/vendor/resonator/{locale}/resonator.php.

Extending

Custom Folder Types

use EkAndreas\Resonator\Models\ResonatorFolder;

ResonatorFolder::create([
    'name' => 'VIP',
    'slug' => 'vip',
    'icon' => 'heroicon-o-star',
    'color' => 'warning',
    'is_system' => false,
]);

Custom Snippets

use EkAndreas\Resonator\Models\ResonatorSnippet;

ResonatorSnippet::create([
    'name' => 'Welcome Message',
    'shortcut' => 'welcome',
    'subject' => 'Welcome to Our Service',
    'body' => '<p>Thank you for reaching out...</p>',
    'is_active' => true,
]);

Listening to Events

// In a service provider
use EkAndreas\Resonator\Models\ResonatorEmail;

ResonatorEmail::created(function ($email) {
    if ($email->is_inbound) {
        // Notify team, trigger webhooks, etc.
    }
});

File Structure

src/
โ”œโ”€โ”€ Actions/
โ”‚   โ”œโ”€โ”€ CleanupOldMessages.php
โ”‚   โ”œโ”€โ”€ SendReply.php
โ”‚   โ””โ”€โ”€ SyncEmails.php
โ”œโ”€โ”€ Commands/
โ”‚   โ””โ”€โ”€ SyncInboxCommand.php
โ”œโ”€โ”€ Filament/
โ”‚   โ””โ”€โ”€ Resources/
โ”‚       โ”œโ”€โ”€ FolderResource.php
โ”‚       โ”œโ”€โ”€ InboxResource.php
โ”‚       โ”œโ”€โ”€ SnippetResource.php
โ”‚       โ””โ”€โ”€ SpamFilterResource.php
โ”œโ”€โ”€ Http/
โ”‚   โ””โ”€โ”€ Integrations/
โ”‚       โ””โ”€โ”€ Resend/
โ”‚           โ”œโ”€โ”€ ResendConnector.php
โ”‚           โ””โ”€โ”€ Requests/
โ”œโ”€โ”€ Jobs/
โ”‚   โ”œโ”€โ”€ DetectSpam.php
โ”‚   โ””โ”€โ”€ EnrichContact.php
โ”œโ”€โ”€ Models/
โ”‚   โ”œโ”€โ”€ ResonatorAttachment.php
โ”‚   โ”œโ”€โ”€ ResonatorContact.php
โ”‚   โ”œโ”€โ”€ ResonatorEmail.php
โ”‚   โ”œโ”€โ”€ ResonatorFolder.php
โ”‚   โ”œโ”€โ”€ ResonatorSnippet.php
โ”‚   โ”œโ”€โ”€ ResonatorSpamFilter.php
โ”‚   โ””โ”€โ”€ ResonatorThread.php
โ”œโ”€โ”€ ResonatorPlugin.php
โ””โ”€โ”€ ResonatorServiceProvider.php

Testing

composer test

Requirements

  • PHP 8.4+
  • Laravel 12.x
  • Filament 4.x
  • Resend account with API key
  • LLM API key (OpenAI, Anthropic, or local Ollama)

Dependencies

Package Version Description
php ^8.4 PHP 8.4 or higher
filament/filament ^4.0 Filament admin panel (includes Livewire 3)
illuminate/contracts ^12.0 Laravel 12 contracts
prism-php/prism ^1.0 AI integration layer
saloonphp/saloon ^3.0 HTTP client for Resend API
spatie/laravel-package-tools ^1.16 Package development utilities
graph TD
    A[filament-resonator] --> B[filament/filament ^4.0]
    A --> C[spatie/laravel-package-tools ^1.16]
    A --> D[saloonphp/saloon ^3.0]
    A --> E[prism-php/prism ^1.0]

    B --> F[Livewire 3]
    B --> G[Laravel 12]

    E --> H[OpenAI]
    E --> I[Anthropic]
    E --> J[Ollama]
Loading

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email security@elseif.se instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.