From 5a11305caf7830e0d7ab3bcd5d7d66a987d1ff75 Mon Sep 17 00:00:00 2001 From: Hickmeister <35031453+Hickmeister@users.noreply.github.com> Date: Sun, 5 Jan 2025 02:28:51 +0000 Subject: [PATCH] Track Filament --- composer.json | 4 +- composer.lock | 1108 +++++++++++++++++- public/addFilament.php | 194 +++ src/filamentTracker/addFilament.php | 131 +++ src/filamentTracker/getFilamentPrices.php | 68 ++ src/filamentTracker/trackFilamentPrices.php | 98 ++ src/filamentTracker/updateFilamentPrices.php | 63 + 7 files changed, 1664 insertions(+), 2 deletions(-) create mode 100644 public/addFilament.php create mode 100644 src/filamentTracker/addFilament.php create mode 100644 src/filamentTracker/getFilamentPrices.php create mode 100644 src/filamentTracker/trackFilamentPrices.php create mode 100644 src/filamentTracker/updateFilamentPrices.php diff --git a/composer.json b/composer.json index bed37d9..eb5fecd 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,7 @@ { "require": { - "php-mqtt/client": "^2.2" + "php-mqtt/client": "^2.2", + "fabpot/goutte": "^4.0", + "symfony/polyfill-ctype": "^1.31" } } diff --git a/composer.lock b/composer.lock index 50aecb7..15669d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,132 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "447c16b903c1f7b070e4b4bfb41716da", + "content-hash": "a6dcfb9667ce99508becf6cd0f9310b9", "packages": [ + { + "name": "fabpot/goutte", + "version": "v4.0.3", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/Goutte.git", + "reference": "e3f28671c87a48a0f13ada1baea0d95acc2138c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/e3f28671c87a48a0f13ada1baea0d95acc2138c3", + "reference": "e3f28671c87a48a0f13ada1baea0d95acc2138c3", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/browser-kit": "^4.4|^5.0|^6.0", + "symfony/css-selector": "^4.4|^5.0|^6.0", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.0" + }, + "type": "application", + "autoload": { + "psr-4": { + "Goutte\\": "Goutte" + }, + "exclude-from-classmap": [ + "Goutte/Tests" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A simple PHP Web Scraper", + "homepage": "https://github.com/FriendsOfPHP/Goutte", + "keywords": [ + "scraper" + ], + "support": { + "issues": "https://github.com/FriendsOfPHP/Goutte/issues", + "source": "https://github.com/FriendsOfPHP/Goutte/tree/v4.0.3" + }, + "abandoned": "symfony/browser-kit", + "time": "2023-04-01T09:05:33+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + }, + "time": "2024-03-31T07:05:07+00:00" + }, { "name": "myclabs/php-enum", "version": "1.8.4", @@ -126,6 +250,59 @@ }, "time": "2024-11-24T20:54:32+00:00" }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, { "name": "psr/log", "version": "3.0.2", @@ -175,6 +352,935 @@ "source": "https://github.com/php-fig/log/tree/3.0.2" }, "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "65d4b3fd9556e4b5b41287bef93c671f8f9f86ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/65d4b3fd9556e4b5b41287bef93c671f8f9f86ab", + "reference": "65d4b3fd9556e4b5b41287bef93c671f8f9f86ab", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/dom-crawler": "^5.4|^6.0|^7.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v6.4.13" + }, + "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-10-25T15:07:50+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/cb23e97813c5837a041b73a6d63a9ddff0778f5e", + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v6.4.13" + }, + "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-25T14:18:03+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "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": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + }, + "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-25T14:20:29+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v6.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "4304e6ad5c894a9c72831ad459f627bfd35d766d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4304e6ad5c894a9c72831ad459f627bfd35d766d", + "reference": "4304e6ad5c894a9c72831ad459f627bfd35d766d", + "shasum": "" + }, + "require": { + "masterminds/html5": "^2.6", + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v6.4.16" + }, + "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-11-13T15:06:22+00:00" + }, + { + "name": "symfony/http-client", + "version": "v6.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "88898d842eb29d7e1a903724c94e90a6ca9c0509" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/88898d842eb29d7e1a903724c94e90a6ca9c0509", + "reference": "88898d842eb29d7e1a903724c94e90a6ca9c0509", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.4.17" + }, + "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-12-18T12:18:31+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" + }, + "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-12-07T08:49:48+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "ea87c8850a54ff039d3e0ab4ae5586dd4e6c0232" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/ea87c8850a54ff039d3e0ab4ae5586dd4e6c0232", + "reference": "ea87c8850a54ff039d3e0ab4ae5586dd4e6c0232", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.4|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.4.17" + }, + "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-12-02T11:09:41+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/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/polyfill-intl-idn", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/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/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "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 for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/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/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "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 for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/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", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "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": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + }, + "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-25T14:20:29+00:00" } ], "packages-dev": [], diff --git a/public/addFilament.php b/public/addFilament.php new file mode 100644 index 0000000..99199a6 --- /dev/null +++ b/public/addFilament.php @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TOD Dashboard + + + + +
+ + + + + + + + +
+
+ + + + + + +
+
+
+
+
Add New Filament to Track
+
+
+ + + +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+ + +
+
+
+
+
+
+
+
+ + + + + + +
+
+ + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/filamentTracker/addFilament.php b/src/filamentTracker/addFilament.php new file mode 100644 index 0000000..ea4bb98 --- /dev/null +++ b/src/filamentTracker/addFilament.php @@ -0,0 +1,131 @@ + 'error', 'message' => 'User not authenticated.']); + exit; +} + +use Goutte\Client; + +// Start session to get user ID +session_start(); +if (!isset($_SESSION['userId'])) { + echo json_encode(['status' => 'error', 'message' => 'User not authenticated.']); + exit; +} + +// Check for POST data +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $filamentName = $_POST['productName'] ?? ''; + $amazonUrl = $_POST['productUrl'] ?? ''; + $userId = $_SESSION['userId']; + + // Basic validation + if (empty($filamentName) || empty($amazonUrl)) { + echo json_encode(['status' => 'error', 'message' => 'Filament name and URL are required.']); + exit; + } + + $client = new Client(); + try { + // Scrape the Amazon page for initial price and other details + $crawler = $client->request('GET', $amazonUrl); + + // Scrape Price + $whole = $crawler->filter('.a-price-whole')->count() ? $crawler->filter('.a-price-whole')->text() : '0'; + $fraction = $crawler->filter('.a-price-fraction')->count() ? $crawler->filter('.a-price-fraction')->text() : '00'; + + $whole = preg_replace('/[^0-9]/', '', $whole); + $fraction = preg_replace('/[^0-9]/', '', $fraction); + $fraction = strlen($fraction) === 1 ? $fraction . '0' : substr($fraction, 0, 2); + $totalPrice = floatval($whole . '.' . $fraction); + + // Scrape Filament Details from Table + $details = []; + $crawler->filter('table.a-normal tr')->each(function ($node) use (&$details) { + $label = trim($node->filter('td.a-span3')->text()); + $value = trim($node->filter('td.a-span9')->text()); + $details[$label] = $value; + }); + + + // Extract details or use default if not found + $brand = $details['Brand'] ?? 'Unknown'; + $material = $details['Material'] ?? 'Unknown'; + $color = $details['Colour'] ?? 'Unknown'; + $filamentWeight = isset($details['Item weight']) ? preg_replace('/[^0-9.]/', '', $details['Item weight']) : 1; + if (stripos($details['Item weight'], 'kilograms') !== false || stripos($details['Item weight'], 'kg') !== false) { + $filamentWeight = $filamentWeight * 1000; // Convert KG to G + } + $itemDiameter = isset($details['Item diameter']) ? preg_replace('/[^0-9.]/', '', $details['Item diameter']) : 1.75; + + // Start a transaction to ensure consistency + $pdo->beginTransaction(); + + // Check if filament already exists + $stmt = $pdo->prepare("SELECT id FROM filamentTracker WHERE amazonUrl = :amazonUrl"); + $stmt->execute([':amazonUrl' => $amazonUrl]); + $existingFilament = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($existingFilament) { + // Filament exists – just update the price history + $filamentId = $existingFilament['id']; + $stmt = $pdo->prepare(" + INSERT INTO filamentPriceHistory (filamentId, price) + VALUES (:filamentId, :price) + "); + $stmt->execute([ + ':filamentId' => $filamentId, + ':price' => $totalPrice + ]); + $message = 'Filament price updated successfully.'; + } else { + // Insert new filament into filamentTracker + $stmt = $pdo->prepare(" + INSERT INTO filamentTracker (userId, filamentName, amazonUrl, filamentWeight, brand, material, color, itemDiameter) + VALUES (:userId, :filamentName, :amazonUrl, :filamentWeight, :brand, :material, :color, :itemDiameter) + "); + $stmt->execute([ + ':userId' => $userId, + ':filamentName' => $filamentName, + ':amazonUrl' => $amazonUrl, + ':filamentWeight' => $filamentWeight, + ':brand' => $brand, + ':material' => $material, + ':color' => $color, + ':itemDiameter' => $itemDiameter + ]); + + // Get the last inserted filament ID + $filamentId = $pdo->lastInsertId(); + + // Insert initial price into filamentPriceHistory + $stmt = $pdo->prepare(" + INSERT INTO filamentPriceHistory (filamentId, price) + VALUES (:filamentId, :price) + "); + $stmt->execute([ + ':filamentId' => $filamentId, + ':price' => $totalPrice + ]); + + $message = $filamentName . ' Filament added successfully and price tracked.'; + } + + // Commit the transaction + $pdo->commit(); + + echo json_encode(['status' => 'success', 'message' => $message]); + } catch (Exception $e) { + $pdo->rollBack(); + echo json_encode(['status' => 'error', 'message' => 'Failed to scrape or insert data. Error: ' . $e->getMessage()]); + } +} else { + echo json_encode(['status' => 'error', 'message' => 'Invalid request method.']); +} +?> diff --git a/src/filamentTracker/getFilamentPrices.php b/src/filamentTracker/getFilamentPrices.php new file mode 100644 index 0000000..ab25444 --- /dev/null +++ b/src/filamentTracker/getFilamentPrices.php @@ -0,0 +1,68 @@ + 'error', 'message' => 'User not authenticated.']); + exit; +} + +header('Content-Type: application/json'); + +// Fetch all filament prices from the database +try { + $stmt = $pdo->query(" + SELECT + ft.filamentName, + ft.brand, + ft.material, + ft.color, + fp.price, + fp.recordedAt + FROM + filamentTracker ft + JOIN + filamentPriceHistory fp ON ft.id = fp.filamentId + ORDER BY + ft.filamentName, + fp.recordedAt ASC + "); + + $filaments = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $result = []; + + // Format data for charts (grouped by filament) + foreach ($filaments as $filament) { + $name = $filament['filamentName']; + + if (!isset($result[$name])) { + $result[$name] = [ + 'brand' => $filament['brand'], + 'material' => $filament['material'], + 'color' => $filament['color'], + 'prices' => [] + ]; + } + + $result[$name]['prices'][] = [ + 'price' => $filament['price'], + 'recordedAt' => $filament['recordedAt'] + ]; + } + + // Return as JSON + echo json_encode([ + 'status' => 'success', + 'data' => $result + ], JSON_PRETTY_PRINT); +} catch (PDOException $e) { + echo json_encode([ + 'status' => 'error', + 'message' => 'Failed to fetch filament prices: ' . $e->getMessage() + ]); +} +?> diff --git a/src/filamentTracker/trackFilamentPrices.php b/src/filamentTracker/trackFilamentPrices.php new file mode 100644 index 0000000..4293255 --- /dev/null +++ b/src/filamentTracker/trackFilamentPrices.php @@ -0,0 +1,98 @@ +request('GET', $productUrl); + + // Extract the current price + $price = $crawler->filter('span.a-price-whole')->first()->text(); + $price = str_replace(',', '', $price); // Clean the price string + + // Extract the original price (if available) + $originalPriceNode = $crawler->filter('span.a-price.a-text-price')->first(); + $originalPrice = $originalPriceNode->count() ? str_replace(',', '', $originalPriceNode->text()) : null; + + // Calculate the discount percentage + $discountPercentage = null; + if ($originalPrice) { + $discountPercentage = round(100 - (($price / $originalPrice) * 100), 2); + } + + $currency = 'GBP'; + + return [ + 'productName' => $productName, + 'productUrl' => $productUrl, + 'price' => $price, + 'originalPrice' => $originalPrice, + 'discountPercentage' => $discountPercentage, + 'currency' => $currency + ]; +} + +// Handle Form Submission (Add New Filament to Track) +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $productName = $_POST['productName']; + $productUrl = $_POST['productUrl']; + + $result = scrapeFilamentPrice($productName, $productUrl); + + try { + $stmt = $pdo->prepare(" + INSERT INTO filamentPrices (productName, productUrl, price, originalPrice, discountPercentage, currency) + VALUES (:productName, :productUrl, :price, :originalPrice, :discountPercentage, :currency) + "); + $stmt->execute([ + ':productName' => $result['productName'], + ':productUrl' => $result['productUrl'], + ':price' => $result['price'], + ':originalPrice' => $result['originalPrice'], + ':discountPercentage' => $result['discountPercentage'], + ':currency' => $result['currency'] + ]); + + echo json_encode(['status' => 'success', 'message' => 'Filament price recorded successfully!']); + } catch (PDOException $e) { + echo json_encode(['status' => 'error', 'message' => 'Failed to record price: ' . $e->getMessage()]); + } +} + +// Handle Scheduled Scraping for Existing URLs (Cron Job) +if (isset($argv[1]) && $argv[1] === 'cron') { + try { + $stmt = $pdo->query("SELECT * FROM filamentPrices GROUP BY productUrl"); + $filaments = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($filaments as $filament) { + $result = scrapeFilamentPrice($filament['productName'], $filament['productUrl']); + + $stmt = $pdo->prepare(" + INSERT INTO filamentPrices (productName, productUrl, price, originalPrice, discountPercentage, currency) + VALUES (:productName, :productUrl, :price, :originalPrice, :discountPercentage, :currency) + "); + $stmt->execute([ + ':productName' => $result['productName'], + ':productUrl' => $result['productUrl'], + ':price' => $result['price'], + ':originalPrice' => $result['originalPrice'], + ':discountPercentage' => $result['discountPercentage'], + ':currency' => $result['currency'] + ]); + + echo "Recorded: {$result['productName']} - £{$result['price']} (Discount: {$result['discountPercentage']}%)\n"; + } + } catch (PDOException $e) { + echo "Failed to fetch filament data: " . $e->getMessage(); + } +} +?> diff --git a/src/filamentTracker/updateFilamentPrices.php b/src/filamentTracker/updateFilamentPrices.php new file mode 100644 index 0000000..ee5ad45 --- /dev/null +++ b/src/filamentTracker/updateFilamentPrices.php @@ -0,0 +1,63 @@ +request('GET', $url); + + $whole = $crawler->filter('.a-price-whole')->count() ? $crawler->filter('.a-price-whole')->text() : '0'; + $fraction = $crawler->filter('.a-price-fraction')->count() ? $crawler->filter('.a-price-fraction')->text() : '00'; + + $whole = preg_replace('/[^0-9]/', '', $whole); + $fraction = preg_replace('/[^0-9]/', '', $fraction); + $fraction = strlen($fraction) === 1 ? $fraction . '0' : substr($fraction, 0, 2); + + $totalPrice = floatval($whole . '.' . $fraction); + + return $totalPrice; + } catch (Exception $e) { + return null; + } +} + +// Fetch all filaments +try { + $stmt = $pdo->query("SELECT * FROM filamentTracker"); + $filaments = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($filaments as $filament) { + $amazonUrl = $filament['amazonUrl']; + $filamentId = $filament['id']; + + // Scrape price + $totalPrice = scrapePrice($amazonUrl); + + if ($totalPrice !== null && $totalPrice > 0) { + $stmt = $pdo->prepare(" + INSERT INTO filamentPriceHistory (filamentId, price) + VALUES (:filamentId, :price) + "); + $stmt->execute([ + ':filamentId' => $filamentId, + ':price' => $totalPrice + ]); + + echo "Updated price for {$filament['filamentName']}: £{$totalPrice}\n"; + } else { + echo "Failed to update {$filament['filamentName']} (no price found or £0).\n"; + } + + // Add a small random delay between 1 to 5 seconds + sleep(rand(1, 5)); + } +} catch (PDOException $e) { + echo "Database error: " . $e->getMessage(); +} +?>