Many Tings
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php-mqtt/client": "^2.2",
|
"php-mqtt/client": "^2.2",
|
||||||
"fabpot/goutte": "^4.0",
|
"fabpot/goutte": "^4.0",
|
||||||
"symfony/polyfill-ctype": "^1.31"
|
"symfony/polyfill-ctype": "^1.31",
|
||||||
|
"phpmailer/phpmailer": "^6.9",
|
||||||
|
"twig/twig": "^3.18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
239
composer.lock
generated
239
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "a6dcfb9667ce99508becf6cd0f9310b9",
|
"content-hash": "8ffb6b03780af1286dc4d1cd3d8f0445",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "fabpot/goutte",
|
"name": "fabpot/goutte",
|
||||||
@@ -250,6 +250,87 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-11-24T20:54:32+00:00"
|
"time": "2024-11-24T20:54:32+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpmailer/phpmailer",
|
||||||
|
"version": "v6.9.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||||
|
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e",
|
||||||
|
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"ext-hash": "*",
|
||||||
|
"php": ">=5.5.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||||
|
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
||||||
|
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3.5",
|
||||||
|
"roave/security-advisories": "dev-latest",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7.2",
|
||||||
|
"yoast/phpunit-polyfills": "^1.0.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
||||||
|
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
||||||
|
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
||||||
|
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
|
||||||
|
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
|
||||||
|
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
||||||
|
"psr/log": "For optional PSR-3 debug logging",
|
||||||
|
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
|
||||||
|
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPMailer\\PHPMailer\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-2.1-only"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Marcus Bointon",
|
||||||
|
"email": "phpmailer@synchromedia.co.uk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jim Jagielski",
|
||||||
|
"email": "jimjag@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Andy Prevost",
|
||||||
|
"email": "codeworxtech@users.sourceforge.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Brent R. Matzelle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||||
|
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Synchro",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-11-24T18:04:13+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/container",
|
"name": "psr/container",
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
@@ -1199,6 +1280,82 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-09-09T11:45:10+00:00"
|
"time": "2024-09-09T11:45:10+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-php81",
|
||||||
|
"version": "v1.31.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||||
|
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||||
|
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"thanks": {
|
||||||
|
"url": "https://github.com/symfony/polyfill",
|
||||||
|
"name": "symfony/polyfill"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Polyfill\\Php81\\": ""
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"Resources/stubs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://symfony.com/sponsor",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-09-09T11:45:10+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/service-contracts",
|
"name": "symfony/service-contracts",
|
||||||
"version": "v3.5.1",
|
"version": "v3.5.1",
|
||||||
@@ -1281,6 +1438,86 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-09-25T14:20:29+00:00"
|
"time": "2024-09-25T14:20:29+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "twig/twig",
|
||||||
|
"version": "v3.18.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/twigphp/Twig.git",
|
||||||
|
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/twigphp/Twig/zipball/acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
||||||
|
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.0.2",
|
||||||
|
"symfony/deprecation-contracts": "^2.5|^3",
|
||||||
|
"symfony/polyfill-ctype": "^1.8",
|
||||||
|
"symfony/polyfill-mbstring": "^1.3",
|
||||||
|
"symfony/polyfill-php81": "^1.29"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^2.0",
|
||||||
|
"psr/container": "^1.0|^2.0",
|
||||||
|
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/Resources/core.php",
|
||||||
|
"src/Resources/debug.php",
|
||||||
|
"src/Resources/escaper.php",
|
||||||
|
"src/Resources/string_loader.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Twig\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com",
|
||||||
|
"homepage": "http://fabien.potencier.org",
|
||||||
|
"role": "Lead Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Twig Team",
|
||||||
|
"role": "Contributors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Armin Ronacher",
|
||||||
|
"email": "armin.ronacher@active-4.com",
|
||||||
|
"role": "Project Founder"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Twig, the flexible, fast, and secure template language for PHP",
|
||||||
|
"homepage": "https://twig.symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"templating"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/twigphp/Twig/issues",
|
||||||
|
"source": "https://github.com/twigphp/Twig/tree/v3.18.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/fabpot",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-12-29T10:51:50+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [],
|
"packages-dev": [],
|
||||||
|
|||||||
162
src/emailService/cache/3e/3eb72c23c9363b64752d0504bdd61ac5.php
vendored
Normal file
162
src/emailService/cache/3e/3eb72c23c9363b64752d0504bdd61ac5.php
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Twig\Environment;
|
||||||
|
use Twig\Error\LoaderError;
|
||||||
|
use Twig\Error\RuntimeError;
|
||||||
|
use Twig\Extension\CoreExtension;
|
||||||
|
use Twig\Extension\SandboxExtension;
|
||||||
|
use Twig\Markup;
|
||||||
|
use Twig\Sandbox\SecurityError;
|
||||||
|
use Twig\Sandbox\SecurityNotAllowedTagError;
|
||||||
|
use Twig\Sandbox\SecurityNotAllowedFilterError;
|
||||||
|
use Twig\Sandbox\SecurityNotAllowedFunctionError;
|
||||||
|
use Twig\Source;
|
||||||
|
use Twig\Template;
|
||||||
|
use Twig\TemplateWrapper;
|
||||||
|
|
||||||
|
/* filament_price_summary.html.twig */
|
||||||
|
class __TwigTemplate_fb2b23b970fb7474e15dec31809a1a27 extends Template
|
||||||
|
{
|
||||||
|
private Source $source;
|
||||||
|
/**
|
||||||
|
* @var array<string, Template>
|
||||||
|
*/
|
||||||
|
private array $macros = [];
|
||||||
|
|
||||||
|
public function __construct(Environment $env)
|
||||||
|
{
|
||||||
|
parent::__construct($env);
|
||||||
|
|
||||||
|
$this->source = $this->getSourceContext();
|
||||||
|
|
||||||
|
$this->parent = false;
|
||||||
|
|
||||||
|
$this->blocks = [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doDisplay(array $context, array $blocks = []): iterable
|
||||||
|
{
|
||||||
|
$macros = $this->macros;
|
||||||
|
// line 1
|
||||||
|
yield "<!DOCTYPE html>
|
||||||
|
<html lang=\"en\">
|
||||||
|
<head>
|
||||||
|
<meta charset=\"UTF-8\">
|
||||||
|
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
|
||||||
|
<link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css\" rel=\"stylesheet\">
|
||||||
|
<title>Filament Price Change Summary</title>
|
||||||
|
<style>
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.price-drop {
|
||||||
|
color: #28a745; /* Green for price drop */
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.price-rise {
|
||||||
|
color: #dc3545; /* Red for price rise */
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class=\"container py-5\">
|
||||||
|
<h1 class=\"mb-4\">Filament Price Change Summary</h1>
|
||||||
|
<p class=\"lead\">Here's the latest update on filament prices from your tracker:</p>
|
||||||
|
<div class=\"table-container\">
|
||||||
|
<table class=\"table table-striped table-bordered\">
|
||||||
|
<thead class=\"thead-dark\">
|
||||||
|
<tr>
|
||||||
|
<th>Filament Name</th>
|
||||||
|
<th>Brand</th>
|
||||||
|
<th>New Price</th>
|
||||||
|
<th>Old Price</th>
|
||||||
|
<th>Change</th>
|
||||||
|
<th>Amazon Link</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
";
|
||||||
|
// line 39
|
||||||
|
$context['_parent'] = $context;
|
||||||
|
$context['_seq'] = CoreExtension::ensureTraversable(($context["filaments"] ?? null));
|
||||||
|
foreach ($context['_seq'] as $context["_key"] => $context["filament"]) {
|
||||||
|
// line 40
|
||||||
|
yield " <tr>
|
||||||
|
<td>";
|
||||||
|
// line 41
|
||||||
|
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["filament"], "filamentName", [], "any", false, false, false, 41), "html", null, true);
|
||||||
|
yield "</td>
|
||||||
|
<td>";
|
||||||
|
// line 42
|
||||||
|
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["filament"], "brand", [], "any", false, false, false, 42), "html", null, true);
|
||||||
|
yield "</td>
|
||||||
|
<td>";
|
||||||
|
// line 43
|
||||||
|
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["filament"], "newPrice", [], "any", false, false, false, 43), "html", null, true);
|
||||||
|
yield "</td>
|
||||||
|
<td>";
|
||||||
|
// line 44
|
||||||
|
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["filament"], "oldPrice", [], "any", false, false, false, 44), "html", null, true);
|
||||||
|
yield "</td>
|
||||||
|
<td class=\"price-drop\">";
|
||||||
|
// line 45
|
||||||
|
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["filament"], "priceChange", [], "any", false, false, false, 45), "html", null, true);
|
||||||
|
yield "</td>
|
||||||
|
<td><a href=\"";
|
||||||
|
// line 46
|
||||||
|
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(CoreExtension::getAttribute($this->env, $this->source, $context["filament"], "amazonUrl", [], "any", false, false, false, 46), "html", null, true);
|
||||||
|
yield "\" target=\"_blank\">View on Amazon</a></td>
|
||||||
|
</tr>
|
||||||
|
";
|
||||||
|
}
|
||||||
|
$_parent = $context['_parent'];
|
||||||
|
unset($context['_seq'], $context['_key'], $context['filament'], $context['_parent']);
|
||||||
|
$context = array_intersect_key($context, $_parent) + $_parent;
|
||||||
|
// line 49
|
||||||
|
yield " </tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class=\"mt-4\">
|
||||||
|
Visit your <a href=\"";
|
||||||
|
// line 53
|
||||||
|
yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape(($context["dashboardUrl"] ?? null), "html", null, true);
|
||||||
|
yield "\" target=\"_blank\">dashboard</a> for more details.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
";
|
||||||
|
yield from [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function getTemplateName(): string
|
||||||
|
{
|
||||||
|
return "filament_price_summary.html.twig";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function isTraitable(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function getDebugInfo(): array
|
||||||
|
{
|
||||||
|
return array ( 124 => 53, 118 => 49, 109 => 46, 105 => 45, 101 => 44, 97 => 43, 93 => 42, 89 => 41, 86 => 40, 82 => 39, 42 => 1,);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSourceContext(): Source
|
||||||
|
{
|
||||||
|
return new Source("", "filament_price_summary.html.twig", "/mnt/www-live/TechOdyssey_Designs_Dashboard/src/emailService/templates/filament_price_summary.html.twig");
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/emailService/css/email_styles.css
Normal file
9
src/emailService/css/email_styles.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
54
src/emailService/emailService.php
Normal file
54
src/emailService/emailService.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
|
||||||
|
require_once '../envLoader.php';
|
||||||
|
loadEnv('../../.env');
|
||||||
|
|
||||||
|
require '../../vendor/autoload.php';
|
||||||
|
|
||||||
|
function sendEmail($to, $subject, $template, $variables = []) {
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Load the template
|
||||||
|
$templatePath = __DIR__ . "/templates/$template.html";
|
||||||
|
if (!file_exists($templatePath)) {
|
||||||
|
throw new Exception("Template not found: $template");
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = file_get_contents($templatePath);
|
||||||
|
|
||||||
|
// Replace variables
|
||||||
|
foreach ($variables as $key => $value) {
|
||||||
|
print_r($key);
|
||||||
|
$body = str_replace("{{" . $key . "}}", strval($value), $body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add environment-based dynamic replacements
|
||||||
|
$body = str_replace("{{website_url}}", strval($_ENV['WEBSITE_URL']), $body);
|
||||||
|
$body = str_replace("{{business_name}}", strval($_ENV['BUSINESS_NAME']), $body);
|
||||||
|
|
||||||
|
// SMTP Configuration
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = $_ENV['SMTP_HOST'];
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = $_ENV['SMTP_USER'];
|
||||||
|
$mail->Password = $_ENV['SMTP_PASS'];
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||||
|
$mail->Port = $_ENV['SMTP_PORT'];
|
||||||
|
|
||||||
|
// Email Settings
|
||||||
|
$mail->setFrom($_ENV['SMTP_USER'], $_ENV['BUSINESS_NAME']);
|
||||||
|
$mail->addAddress($to);
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Body = $body;
|
||||||
|
|
||||||
|
$mail->send();
|
||||||
|
echo "Email sent successfully to $to";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Error: {$mail->ErrorInfo}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
60
src/emailService/sendPriceDropEmailService.php
Normal file
60
src/emailService/sendPriceDropEmailService.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
use Twig\Environment;
|
||||||
|
use Twig\Loader\FilesystemLoader;
|
||||||
|
|
||||||
|
require_once '../envLoader.php';
|
||||||
|
loadEnv('../../.env');
|
||||||
|
|
||||||
|
require '../../vendor/autoload.php';
|
||||||
|
|
||||||
|
function sendEmail($to, $subject, $template, $variables = []) {
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Set up Twig
|
||||||
|
$loader = new FilesystemLoader(__DIR__ . '/templates');
|
||||||
|
$twig = new Environment($loader, [
|
||||||
|
'cache' => __DIR__ . '/cache', // Optional: Enable Twig cache
|
||||||
|
'auto_reload' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Render the Twig template
|
||||||
|
$body = $twig->render($template . '.html.twig', $variables);
|
||||||
|
|
||||||
|
// SMTP Configuration
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = $_ENV['SMTP_HOST'];
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = $_ENV['SMTP_USER'];
|
||||||
|
$mail->Password = $_ENV['SMTP_PASS'];
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||||
|
$mail->Port = $_ENV['SMTP_PORT'];
|
||||||
|
|
||||||
|
// Email Charset and Settings
|
||||||
|
$mail->CharSet = 'UTF-8'; // Ensure correct encoding
|
||||||
|
$mail->setFrom($_ENV['SMTP_USER'], $_ENV['BUSINESS_NAME']);
|
||||||
|
// Handle multiple recipients
|
||||||
|
if (is_array($to)) {
|
||||||
|
foreach ($to as $recipient) {
|
||||||
|
$mail->addAddress($recipient);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$mail->addAddress($to); // Single recipient
|
||||||
|
}
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Body = $body;
|
||||||
|
|
||||||
|
$mail->send();
|
||||||
|
foreach ($to as $recipient) {
|
||||||
|
echo "Email sent successfully to $recipient\n";
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Error: {$mail->ErrorInfo}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
19
src/emailService/templates/alert_error.html
Normal file
19
src/emailService/templates/alert_error.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<title>Error Alert</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<h4 class="alert-heading">Error!</h4>
|
||||||
|
<p>{{message}}</p>
|
||||||
|
<hr>
|
||||||
|
<p class="mb-0">For more details, visit our <a href="https://yourwebsite.com">dashboard</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
src/emailService/templates/alert_info.html
Normal file
19
src/emailService/templates/alert_info.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<title>Info Alert</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<h4 class="alert-heading">Information</h4>
|
||||||
|
<p>{{message}}</p>
|
||||||
|
<hr>
|
||||||
|
<p class="mb-0">For more details, visit our <a href="https://yourwebsite.com">dashboard</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
src/emailService/templates/alert_success.html
Normal file
19
src/emailService/templates/alert_success.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<title>Success Alert</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
<h4 class="alert-heading">Success!</h4>
|
||||||
|
<p>{{message}}</p>
|
||||||
|
<hr>
|
||||||
|
<p class="mb-0">For more details, visit our <a href="https://yourwebsite.com">dashboard</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
57
src/emailService/templates/filament_price_summary.html.twig
Normal file
57
src/emailService/templates/filament_price_summary.html.twig
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<title>Filament Price Change Summary</title>
|
||||||
|
<style>
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.price-drop {
|
||||||
|
color: #28a745; /* Green for price drop */
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.price-rise {
|
||||||
|
color: #dc3545; /* Red for price rise */
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container py-5">
|
||||||
|
<h1 class="mb-4">Filament Price Change Summary</h1>
|
||||||
|
<p class="lead">Here's the latest update on filament prices from your tracker:</p>
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead class="thead-dark">
|
||||||
|
<tr>
|
||||||
|
<th>Filament Name</th>
|
||||||
|
<th>Brand</th>
|
||||||
|
<th>New Price</th>
|
||||||
|
<th>Old Price</th>
|
||||||
|
<th>Change</th>
|
||||||
|
<th>Amazon Link</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for filament in filaments %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ filament.filamentName }}</td>
|
||||||
|
<td>{{ filament.brand }}</td>
|
||||||
|
<td>{{ filament.newPrice }}</td>
|
||||||
|
<td>{{ filament.oldPrice }}</td>
|
||||||
|
<td class="price-drop">{{ filament.priceChange }}</td>
|
||||||
|
<td><a href="{{ filament.amazonUrl }}" target="_blank">View on Amazon</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<p class="mt-4">
|
||||||
|
Visit your <a href="{{ dashboardUrl }}" target="_blank">dashboard</a> for more details.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
132
src/filamentTracker/priceDropEmailCheck.php
Normal file
132
src/filamentTracker/priceDropEmailCheck.php
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
require '../../vendor/autoload.php';
|
||||||
|
require '../db.php';
|
||||||
|
require_once '../envLoader.php';
|
||||||
|
loadEnv('../../.env');
|
||||||
|
|
||||||
|
require_once '../emailService/sendPriceDropEmailService.php';
|
||||||
|
|
||||||
|
// Fetch list of all filaments being tracked
|
||||||
|
function getFilaments() {
|
||||||
|
global $pdo;
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->query("SELECT id, filamentName, brand, amazonUrl FROM filamentTracker");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error fetching filaments: " . $e->getMessage();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the latest prices for each filament
|
||||||
|
function getCurrentPrices() {
|
||||||
|
global $pdo;
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT fp.filamentId, fp.price, fp.recordedAt
|
||||||
|
FROM filamentPriceHistory fp
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT filamentId, MAX(recordedAt) AS latestRecordedAt
|
||||||
|
FROM filamentPriceHistory
|
||||||
|
GROUP BY filamentId
|
||||||
|
) latest ON fp.filamentId = latest.filamentId AND fp.recordedAt = latest.latestRecordedAt
|
||||||
|
");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error fetching current prices: " . $e->getMessage();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch prices from the last 24 hours for each filament
|
||||||
|
function getPriceHistoryForLast24Hours() {
|
||||||
|
global $pdo;
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT filamentId, price, recordedAt
|
||||||
|
FROM filamentPriceHistory
|
||||||
|
WHERE recordedAt >= NOW() - INTERVAL 1 DAY
|
||||||
|
ORDER BY filamentId, recordedAt DESC
|
||||||
|
");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error fetching price history for the last 24 hours: " . $e->getMessage();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare prices and find drops
|
||||||
|
function checkPriceDropsAndNotify() {
|
||||||
|
$filaments = getFilaments();
|
||||||
|
$currentPrices = getCurrentPrices();
|
||||||
|
$priceHistory = getPriceHistoryForLast24Hours();
|
||||||
|
|
||||||
|
// Index prices by filamentId for quick lookup
|
||||||
|
$currentPricesMap = [];
|
||||||
|
foreach ($currentPrices as $current) {
|
||||||
|
$currentPricesMap[$current['filamentId']] = $current['price'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group price history by filamentId
|
||||||
|
$priceHistoryMap = [];
|
||||||
|
foreach ($priceHistory as $history) {
|
||||||
|
$priceHistoryMap[$history['filamentId']][] = $history['price'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$priceDrops = [];
|
||||||
|
|
||||||
|
foreach ($filaments as $filament) {
|
||||||
|
$filamentId = $filament['id'];
|
||||||
|
$currentPrice = $currentPricesMap[$filamentId] ?? null;
|
||||||
|
|
||||||
|
if ($currentPrice !== null && isset($priceHistoryMap[$filamentId])) {
|
||||||
|
foreach ($priceHistoryMap[$filamentId] as $oldPrice) {
|
||||||
|
if ($currentPrice < (float)$oldPrice) {
|
||||||
|
$priceDrops[] = [
|
||||||
|
'filamentName' => $filament['filamentName'],
|
||||||
|
'brand' => $filament['brand'] ?: 'Unknown',
|
||||||
|
'newPrice' => "£" . number_format($currentPrice, 2),
|
||||||
|
'oldPrice' => "£" . number_format((float)$oldPrice, 2),
|
||||||
|
'priceChange' => "-£" . number_format((float)$oldPrice - $currentPrice, 2),
|
||||||
|
'amazonUrl' => $filament['amazonUrl']
|
||||||
|
];
|
||||||
|
break; // Stop checking once a price drop is detected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRecipients() {
|
||||||
|
global $pdo;
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->query("SELECT `email` FROM `users` WHERE `alertEmails` = 1");
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error fetching recipients: " . $e->getMessage();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send email if there are price drops
|
||||||
|
if (!empty($priceDrops)) {
|
||||||
|
$recipient = getRecipients();
|
||||||
|
$subject = 'Filament Price Change Summary';
|
||||||
|
$data = [
|
||||||
|
'filaments' => $priceDrops,
|
||||||
|
'dashboardUrl' => $_ENV['WEBSITE_URL'] . '/dashboard'
|
||||||
|
];
|
||||||
|
|
||||||
|
sendEmail($recipient, $subject, 'filament_price_summary', $data);
|
||||||
|
|
||||||
|
echo "Email sent for the following price drops:\n";
|
||||||
|
foreach ($priceDrops as $drop) {
|
||||||
|
echo "- {$drop['filamentName']}: Price dropped from {$drop['oldPrice']} to {$drop['newPrice']}\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "No price drops detected.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPriceDropsAndNotify();
|
||||||
|
|
||||||
|
?>
|
||||||
Reference in New Issue
Block a user