glastree_on_gitea
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to `ImapEngine-Laravel` will be documented in this file.
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Steve Bauman <steven_bauman@outlook.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,18 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/directorytree/imapengine/blob/master/art/logo.svg" width="300">
|
||||
</p>
|
||||
|
||||
<p align="center">Working with IMAP doesn't need to be hard.</p>
|
||||
|
||||
<p align="center">ImapEngine provides a simple API for managing mailboxes -- without the PHP extension.</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/directorytree/imapengine-laravel/actions"><img src="https://img.shields.io/github/actions/workflow/status/directorytree/imapengine-laravel/run-tests.yml?branch=master&style=flat-square"></a>
|
||||
<a href="https://packagist.org/packages/directorytree/imapengine-laravel"><img src="https://img.shields.io/packagist/dt/directorytree/imapengine-laravel.svg?style=flat-square"></a>
|
||||
<a href="https://packagist.org/packages/directorytree/imapengine-laravel"><img src="https://img.shields.io/packagist/v/directorytree/imapengine-laravel.svg?style=flat-square"></a>
|
||||
<a href="https://packagist.org/packages/directorytree/imapengine-laravel"><img src="https://img.shields.io/github/license/directorytree/imapengine-laravel?style=flat-square"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://imapengine.com/docs/laravel/introduction">View Documentation</a>
|
||||
</p>
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": "directorytree/imapengine-laravel",
|
||||
"description": "This is my package imapengine-laravel",
|
||||
"keywords": [
|
||||
"Steve Bauman",
|
||||
"laravel",
|
||||
"imap"
|
||||
],
|
||||
"homepage": "https://github.com/directorytree/imapengine-laravel",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Steve Bauman",
|
||||
"email": "steven_bauman@outlook.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"directorytree/imapengine": "^1.19.0",
|
||||
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.14",
|
||||
"nunomaduro/collision": "^7.10|^8.1",
|
||||
"orchestra/testbench": "^8.22|^9.0|^10.0|^11.0",
|
||||
"pestphp/pest": "^2.0|^3.0|^4.0",
|
||||
"pestphp/pest-plugin-arch": "^2.0|^3.0|^4.0",
|
||||
"pestphp/pest-plugin-laravel": "^2.0|^3.0|^4.0",
|
||||
"spatie/laravel-ray": "^1.35"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DirectoryTree\\ImapEngine\\Laravel\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"DirectoryTree\\ImapEngine\\Laravel\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": "@composer run prepare",
|
||||
"prepare": "@php vendor/bin/testbench package:discover --ansi",
|
||||
"analyse": "vendor/bin/phpstan analyse",
|
||||
"test": "vendor/bin/pest",
|
||||
"test-coverage": "vendor/bin/pest --coverage",
|
||||
"format": "vendor/bin/pint"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"phpstan/extension-installer": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"DirectoryTree\\ImapEngine\\Laravel\\ImapServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mailboxes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the IMAP mailboxes your application connects to.
|
||||
| Each mailbox contains its connection settings such as host, port, and
|
||||
| credentials. You are free to add as many mailboxes as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'mailboxes' => [
|
||||
'default' => [
|
||||
'port' => env('IMAP_PORT', 993),
|
||||
'host' => env('IMAP_HOST'),
|
||||
'timeout' => env('IMAP_TIMEOUT', 30),
|
||||
'debug' => env('IMAP_DEBUG', false),
|
||||
'username' => env('IMAP_USERNAME'),
|
||||
'password' => env('IMAP_PASSWORD'),
|
||||
'encryption' => env('IMAP_ENCRYPTION', 'ssl'),
|
||||
'validate_cert' => env('IMAP_VALIDATE_CERT', true),
|
||||
'authentication' => env('IMAP_AUTHENTICATION', 'plain'),
|
||||
'proxy' => [
|
||||
'socket' => env('IMAP_PROXY_SOCKET'),
|
||||
'username' => env('IMAP_PROXY_USERNAME'),
|
||||
'password' => env('IMAP_PROXY_PASSWORD'),
|
||||
'request_fulluri' => env('IMAP_PROXY_REQUEST_FULLURI', false),
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Commands;
|
||||
|
||||
use DirectoryTree\ImapEngine\MessageQueryInterface;
|
||||
|
||||
class ConfigureIdleQuery
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
protected array $with = []
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Configure the query.
|
||||
*/
|
||||
public function __invoke(MessageQueryInterface $query): MessageQueryInterface
|
||||
{
|
||||
if (in_array('flags', $this->with)) {
|
||||
$query->withFlags();
|
||||
}
|
||||
|
||||
if (in_array('body', $this->with)) {
|
||||
$query->withBody();
|
||||
}
|
||||
|
||||
if (in_array('headers', $this->with)) {
|
||||
$query->withHeaders();
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Commands;
|
||||
|
||||
use Carbon\CarbonInterface;
|
||||
use DirectoryTree\ImapEngine\Laravel\Events\MessageReceived;
|
||||
use DirectoryTree\ImapEngine\MessageInterface;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class HandleMessageReceived
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
protected WatchMailbox $command,
|
||||
protected int &$attempts = 0,
|
||||
protected ?CarbonInterface &$lastReceivedAt = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle the message received event.
|
||||
*/
|
||||
public function __invoke(MessageInterface $message): void
|
||||
{
|
||||
$this->command->info(
|
||||
"Message received: [{$message->uid()}]"
|
||||
);
|
||||
|
||||
$this->attempts = 0;
|
||||
|
||||
$this->lastReceivedAt = Date::now();
|
||||
|
||||
Event::dispatch(new MessageReceived($message));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Commands;
|
||||
|
||||
use DirectoryTree\ImapEngine\FolderInterface;
|
||||
use DirectoryTree\ImapEngine\Laravel\Events\MailboxWatchAttemptsExceeded;
|
||||
use DirectoryTree\ImapEngine\Laravel\Facades\Imap;
|
||||
use DirectoryTree\ImapEngine\Laravel\Support\LoopInterface;
|
||||
use DirectoryTree\ImapEngine\MailboxInterface;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Console\Exception\InvalidOptionException;
|
||||
|
||||
class WatchMailbox extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'imap:watch {mailbox} {folder?} {--method=idle} {--with=} {--timeout=30} {--attempts=5} {--debug=false}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Watch a mailbox for new messages.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(LoopInterface $loop): void
|
||||
{
|
||||
if (! in_array($method = $this->option('method'), ['idle', 'poll'])) {
|
||||
throw new InvalidOptionException("Invalid method [{$method}]. Valid options are [idle, poll].");
|
||||
}
|
||||
|
||||
$mailbox = Imap::mailbox($name = $this->argument('mailbox'));
|
||||
|
||||
$with = explode(',', $this->option('with'));
|
||||
|
||||
$this->info("Watching mailbox [$name]...");
|
||||
|
||||
$attempts = 0;
|
||||
|
||||
$lastReceivedAt = null;
|
||||
|
||||
$loop->run(function () use ($mailbox, $name, $with, &$attempts, &$lastReceivedAt) {
|
||||
try {
|
||||
$folder = $this->folder($mailbox);
|
||||
|
||||
match ($this->option('method')) {
|
||||
'idle' => $folder->idle(
|
||||
new HandleMessageReceived($this, $attempts, $lastReceivedAt),
|
||||
new ConfigureIdleQuery($with),
|
||||
$this->option('timeout'),
|
||||
),
|
||||
'poll' => $folder->poll(
|
||||
new HandleMessageReceived($this, $attempts, $lastReceivedAt),
|
||||
new ConfigureIdleQuery($with),
|
||||
$this->option('timeout'),
|
||||
),
|
||||
};
|
||||
} catch (Exception $e) {
|
||||
if ($this->isMessageMissing($e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isDisconnection($e)) {
|
||||
sleep(2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($attempts >= $this->option('attempts')) {
|
||||
$this->info("Exception: {$e->getMessage()}");
|
||||
|
||||
Event::dispatch(
|
||||
new MailboxWatchAttemptsExceeded($name, $attempts, $e, $lastReceivedAt)
|
||||
);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mailbox folder to idle.
|
||||
*/
|
||||
protected function folder(MailboxInterface $mailbox): FolderInterface
|
||||
{
|
||||
return ($folder = $this->argument('folder'))
|
||||
? $mailbox->folders()->findOrFail($folder)
|
||||
: $mailbox->inbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the exception is due to a message missing error.
|
||||
*/
|
||||
protected function isMessageMissing(Exception $e): bool
|
||||
{
|
||||
return Str::contains($e->getMessage(), [
|
||||
'no longer exist',
|
||||
], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the exception is caused by a disconnection.
|
||||
*/
|
||||
protected function isDisconnection(Exception $e): bool
|
||||
{
|
||||
return Str::contains($e->getMessage(), [
|
||||
'connection reset by peer',
|
||||
'temporary system problem',
|
||||
'failed to fetch content',
|
||||
'connection failed',
|
||||
'empty response',
|
||||
'not connected',
|
||||
'no response',
|
||||
'broken pipe',
|
||||
'unavailable',
|
||||
], true);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Events;
|
||||
|
||||
use Carbon\CarbonInterface;
|
||||
use Exception;
|
||||
|
||||
class MailboxWatchAttemptsExceeded
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
public string $mailbox,
|
||||
public int $attempts,
|
||||
public Exception $exception,
|
||||
public ?CarbonInterface $lastReceivedAt = null,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Events;
|
||||
|
||||
use DirectoryTree\ImapEngine\MessageInterface;
|
||||
|
||||
class MessageReceived
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public MessageInterface $message
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Facades;
|
||||
|
||||
use DirectoryTree\ImapEngine\Laravel\ImapManager;
|
||||
use DirectoryTree\ImapEngine\Testing\FakeMailbox;
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
/**
|
||||
* @method static \DirectoryTree\ImapEngine\MailboxInterface mailbox(string $name)
|
||||
* @method static \DirectoryTree\ImapEngine\MailboxInterface swap(string $name, \DirectoryTree\ImapEngine\MailboxInterface $mailbox)
|
||||
*/
|
||||
class Imap extends Facade
|
||||
{
|
||||
/**
|
||||
* Get the registered name of the component.
|
||||
*/
|
||||
protected static function getFacadeAccessor(): string
|
||||
{
|
||||
return ImapManager::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fake the given mailbox.
|
||||
*/
|
||||
public static function fake(
|
||||
string $mailbox,
|
||||
array $config = [],
|
||||
array $folders = [],
|
||||
array $capabilities = []
|
||||
): FakeMailbox {
|
||||
/** @var \DirectoryTree\ImapEngine\Laravel\ImapManager $manager */
|
||||
$manager = static::getFacadeRoot();
|
||||
|
||||
$fake = new FakeMailbox($config, $folders, $capabilities);
|
||||
|
||||
$manager->swap($mailbox, $fake);
|
||||
|
||||
return $fake;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel;
|
||||
|
||||
use DirectoryTree\ImapEngine\Mailbox;
|
||||
use DirectoryTree\ImapEngine\MailboxInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class ImapManager
|
||||
{
|
||||
/**
|
||||
* The IMAP configuration.
|
||||
*/
|
||||
protected array $config = [];
|
||||
|
||||
/**
|
||||
* The mailbox instances.
|
||||
*/
|
||||
protected array $mailboxes = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mailbox instance.
|
||||
*/
|
||||
public function mailbox(string $name): MailboxInterface
|
||||
{
|
||||
if (isset($this->mailboxes[$name])) {
|
||||
return $this->mailboxes[$name];
|
||||
}
|
||||
|
||||
if (! array_key_exists($name, $this->config['mailboxes'] ?? [])) {
|
||||
throw new InvalidArgumentException(
|
||||
"Mailbox [{$name}] is not defined. Please check your IMAP configuration."
|
||||
);
|
||||
}
|
||||
|
||||
return $this->mailboxes[$name] = $this->build($this->config['mailboxes'][$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a mailbox instance.
|
||||
*/
|
||||
public function register(string $name, array $config): static
|
||||
{
|
||||
$this->mailboxes[$name] = $this->build($config);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an on-demand mailbox instance.
|
||||
*/
|
||||
public function build(array $config): MailboxInterface
|
||||
{
|
||||
return new Mailbox($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a mailbox from the in-memory cache.
|
||||
*/
|
||||
public function forget(string $name): static
|
||||
{
|
||||
unset($this->mailboxes[$name]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap out a mailbox instance with a new one.
|
||||
*/
|
||||
public function swap(string $name, MailboxInterface $mailbox): void
|
||||
{
|
||||
$this->mailboxes[$name] = $mailbox;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel;
|
||||
|
||||
use DirectoryTree\ImapEngine\Laravel\Support\Loop;
|
||||
use DirectoryTree\ImapEngine\Laravel\Support\LoopInterface;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class ImapServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(ImapManager::class, function () {
|
||||
return new ImapManager(config('imap', []));
|
||||
});
|
||||
|
||||
$this->app->bind(LoopInterface::class, Loop::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->commands([
|
||||
Commands\WatchMailbox::class,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->publishes([
|
||||
__DIR__.'/../config/imap.php' => config_path('imap.php'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Support;
|
||||
|
||||
class Loop implements LoopInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run(callable $tick): void
|
||||
{
|
||||
while (true) {
|
||||
$tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Support;
|
||||
|
||||
class LoopFake implements LoopInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function run(callable $tick): void
|
||||
{
|
||||
$tick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace DirectoryTree\ImapEngine\Laravel\Support;
|
||||
|
||||
interface LoopInterface
|
||||
{
|
||||
/**
|
||||
* Execute the loop.
|
||||
*/
|
||||
public function run(callable $tick): void;
|
||||
}
|
||||
Reference in New Issue
Block a user