Compare commits

..

No commits in common. "stable" and "1.16.0" have entirely different histories.

248 changed files with 4961 additions and 10327 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
node_modules
vendor

326
README.md
View File

@ -1,158 +1,51 @@
# huesos
Base for creating shop chat-robots using Web App technology for [Telegram](https://telegram.org)
## Functions
1. Tree-structured catalog
2. Product cards with images carousel ([mirzaev/hotline.mjs](https://git.svoboda.works/mirzaev/hotline.mjs))
3. Cart (CRUD with limits and session binding)
4. Saving user data (and session) for all devices
5. Deliveries settings (with interactive maps and automatic geolocation detection on smartphones)
6. Real time price generation
7. Interface according to all Telegram standards
8. Public offer, dynamic settings and suspensions
9. Multi-language and easy to add new languages
10. Multi-currency and easy to add new currencies
11. Loading products and categories from an excel-file with automatic updating of existing ones
12. Flag authorization system, separate access for testers
13. Sending the generated order directly to the chat-robot
14. Intelligent search by titles, descriptions and other parameters (Levenshtein algorithm + separate settings for different languages)
15. Asynchronous chat-robot and Web App based on dynamic queries (AJAX)
16. Modern non-relational database ready for scaling and integration with third-party CRM
17. Fully documented code in English
18. Customizable menu buttons
19. Responsive design with built-in Telegram buttons and haptic functions
20. Automatic download and compression of images in 4 sizes (currently only from Yandex.Disk, but the system is ready to add new sources)
21. Commercially approved fonts and pure CSS icons
22. Product filter panel using pure CSS
23. Damper technology on all user interaction functions ([mirzaev/damper.mjs](https://git.svoboda.works/mirzaev/damper.mjs))
24. Two-step registration system (entering other data after creating an order)
25. Delivery company selection system (ready for scaling)
26. Acquiring company selection system (ready for scaling)
27. Sending paid orders to the operators chat with the customer contacts
## Integrations
### Import
*Methods for importing products into the shop*<br>
1. Excel-file (products and categories)
### Images download
*Methods of transferring images when importing products into the shop*<br>
1. [Yandex.Disk](https://360.yandex.ru/disk/) (russian) ([API](https://yandex.com/dev/disk/))
### Delivery companies
*Companies that deliver products from the shop*<br>
1. [CDEK](https://www.cdek.ru/) (russian) ([API](https://api-docs.cdek.ru/29923741.html)) ([PHP library](https://github.com/TTATPuOT/cdek-sdk2.0))
### Acquiring companies
*Companies that provide acquiring for the shop*<br>
1. [Robokassa](https://robokassa.com) (russian) (no swift) ([API](https://docs.robokassa.ru/pay-interface/))
## Dependencies
1. [PHP 8.4](https://www.php.net/releases/8.4/en.php)
2. [Composer](https://getcomposer.org/) (php package manager)
3. [MINIMAL](https://git.svoboda.works/mirzaev/minimal) (PHP framework)
4. [Twig](https://twig.symfony.com/) (HTML templater)
5. [Zanzara](https://github.com/badfarm/zanzara) (Telegram framework + ReactPHP)
6. [ArangoDB](https://docs.arangodb.com/3.11/about-arangodb/) (non-relational database)
7. [NGINX](https://nginx.org/en/) (web server) *(can be replaced)*
8. [SystemD](https://systemd.io/) (service manager) *(can be replaced)*
<small>You can find other dependencies in the file `/composer.json`</small>
Basis for developing chat-robots with "Web App" technology for Telegram
## Installation
### AnangoDB
1. **Configure unix-socket**<br>
Edit the file `/etc/arangodb3/arangod.conf`<br>
`endpoint = tcp://127.0.0.1:8529` -> `endpoint = unix:///var/run/arangodb3/arango.sock` (this will disable the web panel)<br>
<br>
To make the web panel work, you can add this to the NGINX server settings:
```lua
server {
...
server_name arangodb.domain.zone;
...
allow YOUR_IP_ADDRESS;
allow 192.168.1.1/24;
allow 127.0.0.1;
deny all;
# ArangoDB
location / {
proxy_pass http://arangodb;
}
}
upstream arangodb {
server unix:/var/run/arangodb3/arango.sock;
}
```
[here is my solution for "permission denied" problem on Ubuntu (accepted by ArangoDB maintainer)](https://github.com/arangodb/arangodb/issues/17302)<br>
1. **Configure TCP (instead of unix-socket)**<br>
Edit the file `/etc/arangodb3/arangod.conf`<br>
`endpoint = tcp://127.0.0.1:8529` -> `endpoint = tcp://0.0.0.0:8529`<br>
Edit the file `mirzaev/huesos/system/settings/arangodb.php`<br>
`unix:///var/run/arangodb3/arango.sock` -> `tcp://YOUR_IP_ADDRESS:8529` (it is slow and not secure)
---
2. **Create a Graph with the specified values**<br>
1. Create a Graph with the specified values
**Name:** catalog<br>
* Relation 1<br>
<br>
* Relatin 1
**edgeDefinition:** entry<br>
**fromCollections:** category, product<br>
**fromCollections:** categoy, product<br>
**toCollections:** category
* Relation 2<br>
* Relation 2
**edgeDefinition:** reservation<br>
**fromCollections:** product<br>
**toCollections:** cart
---
3. **Create a Graph with the specified values**<br>
2. Create a Graph with the specified values
**Name:** users<br>
* Relation 1<br>
<br>
* Relation 1
**edgeDefinition:** connect<br>
**fromCollections:** cart, session<br>
**toCollections:** account, session<br>
**toCollections:** account, session
* Orphan Collections<br>
product
---
**Orphan Collections:** product
4. **Create indexes for the "product" collection**<br>
3. Create indexes for the "product" collection
**Type:** "Inverted Index"<br>
**Fields:** name.ru<br>
**Analyzer:** "text_ru"<br>
**Search field:** true<br>
**Name:** name_ru<br><br>
**Name:** name_ru<br>
<br>
*Add indexes for all search parameters and for all languages (search language is selected based on the user's language, <br>
otherwise from the default language specified in the active settings from **settings** collection document)*<br>
<br>
*See fields in the `mirzaev/arming_bot/models/product`<br>
**name.ru**, **description.ru** and **compatibility.ru***<br>
---
5. **Create a View with the specified values**<br>
**name.ru**, **description.ru** and **compatibility.ru***
4. Create a View with the specified values
**type:** search-alias (you can also use "arangosearch")<br>
**name:** **product**s_search<br>
**indexes:**<br><br>
**indexes:**<br>
<br>
You can copy an example of view file from here: `/examples/arangodb/views/products_search.json`
```json
@ -166,177 +59,50 @@ You can copy an example of view file from here: `/examples/arangodb/views/produc
### NGINX
1. **Create a NGINX server**<br>
1. Create a NGINX server
You can copy an example of server file from here: `/examples/nginx/server.conf`
2. **Add support for javascript modules**<br>
2. Add support for javascript modules
Edit the file `/etc/nginx/mime.types`<br>
`application/javascript js;` -> `application/javascript js mjs;`
3. **Generate a TLS/SSL sertificate** (via [certbot](http://certbot.eff.org/) for [ubuntu](https://ubuntu.com/))<br>
3.1. `sudo apt install certbot python3-certbot-nginx`<br>
3.2. `sudo certbot certonly --nginx` (The **domain** must already be **bound** to the **IP-address** of the server by `A-record` or `AAAA-record`)
5. **Firewall rules for HTTP and HTTPS** (for [ubuntu](https://ubuntu.com/))<br>
4.1 `sudo ufw allow "NGINX Full"`<br>
4.1.1 `sudo ufw allow 22` (make sure that the port for SSH connection is open)<br>
4.2 `sudo ufw enable`
### SystemD (or any alternative you like)
You can copy an example of systemd file from here: `/examples/systemd/huesos.service`<br>
1. `sudo cp huesos.service /etc/systemd/system/huesos.service && sudo chmod +x /etc/systemd/system/huesos.service`
2. `sudo systemctl daemon-reload`
3. `sudo systemctl enable huesos`<br>
<br>
**Execute:** `sudo cp huesos.service /etc/systemd/system/huesos.service && sudo chmod +x /etc/systemd/system/huesos.service`<br>
<br>
*before you execute the command think about **what it does** and whether the **paths** are specified correctly*<br>
*the configuration file is very simple and you can remake it for any alternative to SystemD that you like*
## Menu
*Menu inside the Web App*<br><br>
Make sure you have a **menu** collection (can be created automatically)<br>
You can copy a clean menu documents without comments from here: `/examples/arangodb/collections/menu`
```json
{
"urn": "/", // Link
"name": {
"en": "Main page",
"ru": "Главная страница"
},
"style": { // The `style` attribute
"order": 0
},
"class": "",
"icon": { // Icon from `/themes/default/css/icons`
"style": { // The `style` attribute
"rotate": "-135deg"
},
"class": "arrow circle" // Classes of the icon
},
"image": { // Image at the background @deprecated?
"storage": null
}
}
```
## Settings
*Settings of chat-robot and Web App*<br><br>
Settings of chat-robot and Web App<br>
<br>
Make sure you have a **settings** collection (can be created automatically) and at least one document with the "status" parameter set to "active"<br>
You can copy a clean settings document without comments from here: `/examples/arangodb/collections/settings.json`
```json
{
"status": "active", // Values: "active", "inactive" (string) Status of the settings document?
"project": {
"name": "PROJECT" // Name of the projext (string)
},
"language": "en", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\language`
"currency": "usd", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\currency`
"company": {
"identifier": null, // Example: "000000000000000" (string|null) (if `null` it will not be displayed)
"tax": null, // Example: "000000000000" (string|null) (if `null` it will not be displayed)
"name": null, // Example: "COMPANY" (string|null) (if `null` it will not be displayed)
"offer": false, // Display the data of a public offer in the footer? (bool) (does not affect the `/offer` page generation)
"sim": null, // Examples: "+7 000 000-00-00", "70000000000" (string|null) (if `null` it will not be displayed)
"mail": null // Example: "name@domain.zone" (string|null) (if `null` it will not be displayed)
},
"search": {
"enabled": true, // Enable the search input field?
"position": "fixed" // Values: "fixed", "relative"
},
"catalog": {
"categories": {
"display": "column", // Values: "row" (flex wrap), "column" (rows) Type of the catalog display
"structure": "lists", // Values: "pages" (pages with categories and products), "lists" (all categories as tree lists on the main page)
"buttons": {
"height": "120px", // Examples: "80px", "120px", "180px" (string|null) Height of buttons
"background": "#fafafa", // Examples: "#fafafa", "yellow" (string|null) Color of buttons background
"separator": {
"enabled": true, // Enable separators?
"width": "60%" // Exaples: "100%", "80%", "60%" (string|null) Width of separators over images (relative to image width from the left)
},
"lists": {
"height": "800px", // Examples: "500px", "100%" (string|null) Maximum height of lists (`max-height` for animations working)
"background": null, // Examples: "#fafafa", "yellow" (string|null) Color of lists
"separator": null, // Examples: "#fafafa", "yellow" (string|null) Color of separators between rows
"separated": true, // Separate lists from its buttons?
"blocks": true, // Blocks instead of plain text?
"arrow": true // Add arrow at the right?
},
"texts": {
"position": {
"vertical": "center" // Values: "top", "center", "bottom" (string|null) Position of texts of ascendants categories
},
"width": "max(8rem, 20%)", // Examples: "60%", "5rem", "max(8rem, 20%)" (string|null) Width of the text section (left side of buttons)
"background": false, // Enable wrapping element for texts?
"title": {
"color": "#020202" // Examples: "#fafafa", "yellow" (string|null) Color of titles
},
"description": {
"color": "#121212" // Examples: "#fafafa", "yellow" (string|null) Color of descriptions
}
},
"images": {
"filter": "contrast(1.2)" // Example: "contrast(1.2)" (string|null) Filter for images
}
}
"status": "active",
"project": {
"name": "PROJECT"
},
"language": "en", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\language`
"currency": "usd", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\currency`
"company": {
"identifier": null, // Example: "000000000000000" (string|null) (if `null` it will not be displayed)
"tax": null, // Example: "000000000000" (string|null) (if `null` it will not be displayed)
"name": null, // Example: "COMPANY" (string|null) (if `null` it will not be displayed)
"offer": false, // Display the data of a public offer in the footer? (bool) (does not affect the `/offer` page generation)
"sim": null, // Examples: "+7 000 000-00-00", "70000000000" (string|null) (if `null` it will not be displayed)
"mail": null // Example: "name@domain.zone" (string|null) (if `null` it will not be displayed)
}
},
"cart": {
"enabled": true // Enable the cart button?
},
"css": {
"catalog-button-cart-background": "#40a7e3",
"product-button-cart-background": "#40a7e3"
"catalog-button-cart-added-background": "#90be36",
"product-button-cart-added-background": "#90be36"
},
"account": {
"enabled": false // Enable the account section? (works only when opened from telegram `inline-button`)
},
"menu": {
"enabled": true, // Enable the main menu?
"position": "fixed" // Values: "fixed" (fixed to the bottom as a solid line), "relative" (at the top as separated buttons) (stirng) Position of the main menu
},
"header": {
"enabled": true, // Enable the header?
"position": "fixed" // Values: "fixed" (fixed to the bottom), "relative" (at the top) (stirng) Position of the header
},
"acquirings": {
"robokassa": {
"enabled": true, // Enable the Robokassa acquiring?
"mode": "test" // Values: "work", "test" (string) Mode of the Robokassa acquiring
}
},
"input": {
"deliveries": {
"site": [], // Values: "sim", "name", "destination", "address", "commentary"
"chat": [
"sim",
"name",
"destination",
"address",
"commentary"
] // Values: "sim", "name", "destination", "address", "commentary"
}
},
"deliveries": {
"cdek": {
"enabled": true, // Enable CDEK delivery?
"label": "CDEK" // Name of the CDEK delivery
}
},
"chats": [
{
"id": null, // Example: -1002599391893 (int) (negative number) The telegram chat identifier
"orders": true // Send orders? (for moderators)
}
]
}
```
## Suspensions
*System of suspensions of chat-robot and Web App*<br><br>
System of suspensions of chat-robot and Web App<br>
<br>
Make sure you have a **suspension** collection (can be created automatically)<br>
You can copy a clean suspension document without comments from here: `/examples/arangodb/collections/suspension.json`
```json
@ -356,13 +122,3 @@ You can copy a clean suspension document without comments from here: `/examples/
}
}
```
## Used by
*List of projects created on the basis of [huesos](https://git.svoboda.works/mirzaev/huesos)*
- ARMING [@arming_bot](https://t.me/arming_bot)<br>
*Russian weapons tuning shop* ([repository](https://git.svoboda.works/mirzaev/arming))

View File

@ -1,7 +1,7 @@
{
"name": "mirzaev/huesos",
"name": "mirzaev/arming_bot",
"description": "Chat-robot for tuning weapons",
"homepage": "https://t.me/huesos",
"homepage": "https://t.me/arming_bot",
"type": "chat-robot",
"keywords": [
"telegram",
@ -20,10 +20,9 @@
"require": {
"php": "^8.4",
"ext-gd": "^8.4",
"ext-intl": "^8.4",
"triagens/arangodb": "^3.8",
"mirzaev/minimal": "^3.4.0",
"mirzaev/arangodb": "^2",
"mirzaev/minimal": "^2.2",
"mirzaev/arangodb": "^1.3",
"badfarm/zanzara": "^0.9.1",
"nyholm/psr7": "^1.8",
"react/filesystem": "^0.1.2",
@ -33,12 +32,11 @@
"avadim/fast-excel-reader": "^2.19",
"ttatpuot/cdek-sdk2.0": "^1.2",
"guzzlehttp/guzzle": "^7.9",
"php-http/guzzle7-adapter": "^1.0",
"react/async": "^4.3"
"php-http/guzzle7-adapter": "^1.0"
},
"autoload": {
"psr-4": {
"mirzaev\\huesos\\": "mirzaev/huesos/system/"
"mirzaev\\arming_bot\\": "mirzaev/arming_bot/system/"
}
},
"minimum-stability": "stable",

770
composer.lock generated Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +0,0 @@
{
"urn": "/account",
"name": {
"en": "Account",
"ru": "Аккаунт"
},
"identifier": "account",
"style": {
"order": 1
},
"class": "",
"icon": {
"style": {},
"class": "loading spinner animated"
},
"image": {
"storage": null
}
}

View File

@ -1,18 +0,0 @@
{
"urn": "/cart",
"name": {
"en": "Cart",
"ru": "Корзина"
},
"style": {
"order": 999
},
"class": "cart",
"icon": {
"style": {},
"class": "shopping cart"
},
"image": {
"storage": null
}
}

View File

@ -1,18 +0,0 @@
{
"urn": "/",
"name": {
"en": "Main page",
"ru": "Главная страница"
},
"style": {
"order": 0
},
"class": "",
"icon": {
"style": {},
"class": "house"
},
"image": {
"storage": null
}
}

View File

@ -1,27 +1,16 @@
{
"status": "active",
"project": {
"name": "PROJECT"
},
"language": "en",
"currency": "usd",
"company": {
"identifier": null,
"tax": null,
"name": null,
"offer": false,
"sim": null,
"mail": null
},
"search": {
"enabled": true,
"position": "fixed"
},
"cart": {
"enabled": true
},
"css": {
"catalog-button-cart-background": "#40a7e3",
"product-button-cart-background": "#40a7e3"
}
"status": "active",
"project": {
"name": "PROJECT"
},
"language": "en",
"currency": "usd",
"company": {
"identifier": null,
"tax": null,
"name": null,
"offer": false,
"sim": null,
"mail": null
}
}

View File

@ -1,41 +1,37 @@
#
# This section is commented out to make it possible to run NGINX without errors
# to generate TLS/SSL certificate via CertBot (see README.md)
#
# server {
# listen 443 default_server ssl;
# listen [::]:443 ssl default_server;
server {
listen 443 ssl;
listen [::]:443 ssl ipv6only=on;
# server_name domain.zone;
server_name domain.zone;
# root /var/www/huesos/mirzaev/huesos/system/public;
root /var/www/project/mirzaev/huesos/system/public;
# index index.php;
index index.php;
# ssl_certificate /etc/letsencrypt/live/domain.zone/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/domain.zone/privkey.pem;
# include /etc/letsencrypt/options-ssl-nginx.conf;
# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
ssl_certificate /etc/letsencrypt/live/domain.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# location / {
# try_files $uri $uri/ /index.php?$query_string;
# }
location / {
try_files $uri $uri/ /index.php;
}
# location /api/cdek {
# rewrite ^/api/cdek(.*)$ /$1 break;
# index cdek.php;
# }
location /api/cdek {
rewrite ^/api/cdek(.*)$ /$1 break;
index cdek.php;
}
# location ~ /(?<type>categories|products) {
# root /var/www/huesos/mirzaev/huesos/system/storage;
# try_files $uri =404;
# }
location ~ /(?<type>categories|products) {
root /var/www/arming_bot/mirzaev/arming_bot/system/storage;
try_files $uri =404;
}
# location ~ \.php$ {
# include snippets/fastcgi-php.conf;
# fastcgi_pass unix:/run/php/php8.4-fpm.sock;
# }
# }
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
}
server {
listen 80 default_server;

View File

@ -5,7 +5,7 @@ Wants=network.target
After=syslog.target network-online.target
[Service]
ExecStart=sudo -u www-data /usr/bin/php /var/www/huesos/mirzaev/huesos/system/public/robot.php
ExecStart=sudo -u www-data /usr/bin/php /var/www/project/mirzaev/huesos/system/public/robot.php
PIDFile=/var/run/php/huesos.pid
RemainAfterExit=no
RuntimeMaxSec=3600s

View File

@ -2,10 +2,10 @@
declare(strict_types=1);
namespace mirzaev\huesos\controllers;
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\huesos\controllers\core;
use mirzaev\arming_bot\controllers\core;
// Framework for PHP
use mirzaev\minimal\http\enumerations\status;
@ -13,7 +13,7 @@ use mirzaev\minimal\http\enumerations\status;
/**
* Controller of account
*
* @package mirzaev\huesos\controllers
* @package mirzaev\arming_bot\controllers
*
* @param array $errors Registry of errors
*

View File

@ -2,34 +2,23 @@
declare(strict_types=1);
namespace mirzaev\huesos\controllers;
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\huesos\controllers\core,
mirzaev\huesos\models\cart as model,
mirzaev\huesos\models\product,
mirzaev\huesos\models\menu,
mirzaev\huesos\models\telegram,
mirzaev\huesos\models\enumerations\currency,
mirzaev\huesos\models\enumerations\language;
use mirzaev\arming_bot\controllers\core,
mirzaev\arming_bot\models\cart as model,
mirzaev\arming_bot\models\product,
mirzaev\arming_bot\models\menu,
mirzaev\arming_bot\models\enumerations\language;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content,
mirzaev\minimal\http\enumerations\status;
// Framework for Telegram
use Zanzara\Zanzara as zanzara,
Zanzara\Context as context,
Zanzara\Config as config,
Zanzara\Telegram\Type\Message as message;
// Event manager for PHP
use React\EventLoop\Loop as loop;
/**
* Controller of cart
*
* @package mirzaev\huesos\controllers
* @package mirzaev\arming_bot\controllers
*
* @param model|null $cart Instance of the cart
* @param array $errors Registry of errors
@ -85,9 +74,6 @@ final class cart extends core
parameters: ['language' => $this->language->name],
errors: $this->errors['menu']
);
// Universalizing
if ($this->view->menu instanceof _document) $this->view->menu = [$this->view->menu];
}
// Initializing the cart
@ -124,26 +110,12 @@ final class cart extends core
$this->view->formatted = $formatted;
}
if (array_search('address', $this->settings->input['deliveries']['site'], true) !== false) {
// The deliveries input is enabled for the site
// Declaring the deliveries list
$deliveries = [];
foreach ($this->settings->deliveries as $key => $value) {
// Iterating over deliveries
if ($value['enabled']) {
// The delivery is enabled
// Writing into the deliveries list
$deliveries[$key] = ['label' => $value['label']];
}
}
// Writing the deliveries list into the templater variables
$this->view->deliveries = $deliveries;
}
// Initializing types of avaiabld deliveries
$this->view->deliveries = [
'cdek' => [
'label' => 'CDEK'
]
];
if (str_contains($this->request->headers['accept'], content::json->value)) {
// Request for JSON response
@ -469,147 +441,85 @@ final class cart extends core
}
/**
* Attach
* Pay
*
* Attach the cart
* Pay for the cart
*
* @return null
*
* @todo кажется я сделал хуйню
*/
public function attach(?string $share = null): null
public function pay(): null
{
if (str_contains($this->request->headers['accept'], content::json->value)) {
// Request for JSON response
if ($this->account) {
// Initialized the account
// Initializing the cart
$this->cart ??= $this->account?->cart() ?? $this->session?->cart();
$config = new config();
$config->setParseMode(config::PARSE_MODE_MARKDOWN);
$config->useReactFileSystem(true);
/* $config->setLoop(loop::get()); */
if ($this->cart instanceof model) {
// Initialized the cart
$robot = new zanzara(TELEGRAM_KEY, $config);
if ($share = $this->cart->share ?? $this->cart->share()) {
// The cart is available for sharing
// Initializing cart
$cart = model::_read(
filter: 'd.share == @share',
sort: 'd.updated DESC, d.created DESC, d._key DESC',
amount: 1,
page: 1,
parameters: ['share' => $share]
);
// Sending response
$this->response
->start()
->clean()
->sse()
->json([
'share' => $share,
'errors' => $this->errors
])
->validate($this->request)
?->body()
->end();
}
// Deinitializing unnecessary variables
unset($share);
if ($cart instanceof model) {
// Initialized the cart
// Unsharing the cart
$cart->unshare();
// Connecting the cart to the account
$edge = $this->account->connect($cart);
if (!empty($edge)) {
// Connected the cart to the account
// Initializing products in the cart
$products = $cart->products(language: $this->account->language ?? language::ru, currency: $this->account->currency ?? currency::rub);
if (!empty($products)) {
// Initialized products in the cart
// Declaring the formatted list of products for message
$list = '';
// Declaring total cost of products
$cost = $cart->cost($list);
// Escaping the formatted list of products for the message
$list = telegram::unmarkdown($list);
// Deinitializing unnecessary variables
unset($products, $product, $row);
// Initializing identifier of the cart
$identifier = $cart->getKey();
// Initializing currency symbol
$symbol = ($this->account->currency ?? currency::rub)->symbol();
// Declaring delivery texts
$delivery_cost = $delivery_days = $delivery_address = '';
if (!empty($cart->buffer['delivery'])) {
// Initialized delibery data
// Initializing delivery cost for message
$delivery_cost = telegram::unmarkdown((string) $cart->buffer['delivery']['cost']);
// Initializing delivery days for message
$delivery_days = telegram::unmarkdown((string) $cart->buffer['delivery']['days']);
// Initializing delivery address for message
$delivery_address = telegram::unmarkdown($cart->buffer['delivery']['location']['name'] . ', ' . $cart->buffer['delivery']['street']);
}
// Initializing the message cost part
$part_cost = "*Стоимость:* $cost$symbol";
if (!empty($delivery_cost)) $part_cost .= " \+ $delivery_cost$symbol";
if (!empty($delivery_days)) $part_cost .= " \($delivery_days дней\)";
// Initializing the message delivery part
$part_delivery = '';
if (!empty($delivery_address)) $part_cost .= "*Адрес доставки:* $delivery_address";
if (!empty($part_delivery)) $part_delivery = "\n$part_delivery";
$robot->getTelegram()->sendMessage(
<<<TXT
🛒 *Добавлена корзина* \#$identifier
$list
$part_cost$part_delivery
TXT,
[
'chat_id' => $this->account->identifier,
'reply_markup' => [
'inline_keyboard' => [
[
['text' => '🚚 Оформить доставку', 'callback_data' => 'cart_delivery'],
],
],
'disable_notification' => true
]
]
);
}
}
// Deinitializing unnecessary variables
unset($cart, $list);
}
unset($hash);
}
}
// Deinitializing unnecessary variables
unset($cart);
// Exit (success/fail)
return null;
}
$robot->getLoop()->run();
/**
* Robokassa
*
* HTML-document with robokassa iframe
*
* @param null
*
* @todo THIS MUST BE A PAYMENT OF ORDER IN THE FUTURE, NOT CART
*/
public function robokassa(): null
{
// Initializing the cart
$this->cart ??= $this->account?->cart() ?? $this->session?->cart();
// Initializing the cart data
$this->view->cart = $this->cart;
$this->view->summary = $this->cart?->summary(currency: $this->currency);
if (str_contains($this->request->headers['accept'], content::any->value)) {
// Request for any response
// Render page
$page = $this->view->render('iframes/robokassa.html');
// Sending response
$this->response
->start()
->clean()
->sse()
->json([
'success' => true,
'errors' => $this->errors
])
->write($page)
->validate($this->request)
?->body()
->end();
// Deinitializing rendered page
unset($page);
}
// Exit (success/fail)

View File

@ -2,17 +2,16 @@
declare(strict_types=1);
namespace mirzaev\huesos\controllers;
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\huesos\controllers\core,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\catalog as model,
mirzaev\huesos\models\entry,
mirzaev\huesos\models\category,
mirzaev\huesos\models\product,
mirzaev\huesos\models\cart,
mirzaev\huesos\models\menu;
use mirzaev\arming_bot\controllers\core,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\entry,
mirzaev\arming_bot\models\category,
mirzaev\arming_bot\models\product,
mirzaev\arming_bot\models\cart,
mirzaev\arming_bot\models\menu;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content,
@ -25,7 +24,7 @@ use ArangoDBClient\Document as _document;
/**
* Controller of catalog
*
* @package mirzaev\huesos\controllers
* @package mirzaev\arming_bot\controllers
*
* @param cart|null $cart Instance of the cart
* @param array $errors Registry of errors
@ -53,8 +52,7 @@ final class catalog extends core
'session' => [],
'account' => [],
'menu' => [],
'catalog' => [],
'cart' => []
'catalog' => []
];
/**
@ -82,13 +80,9 @@ final class catalog extends core
// Initializing the cart
$this->cart ??= $this->account?->cart() ?? $this->session?->cart();
// Initializing summary data of the cart
$summary = $this->cart?->summary(currency: $this->currency, errors: $this->errors['cart']);
// Initializing the cart data
$this->view->cart = [
'products' => $this->cart?->products(language: $this->language, currency: $this->currency),
'summary' => $summary
'products' => $this->cart?->products(language: $this->language, currency: $this->currency)
];
// Validating received product identifier
@ -119,7 +113,6 @@ final class catalog extends core
'cart' => [
'amount' => $this->view->cart['products'][$this->view->product->getId()]['amount'] ?? 0,
'text' => [
'cart' => $this->language === language::ru ? 'Корзина' : 'Cart',
'add' => $this->language === language::ru ? 'Добавить в корзину' : 'Add to the cart',
'added' => $this->language === language::ru ? 'Добавлено в корзину' : 'Added to the cart'
]
@ -196,26 +189,31 @@ final class catalog extends core
// Write to the response buffer
$response['category'] = ['name' => $category->name ?? null];
// Searching for entries that are descendants of $category
$search = model::search(
// Search for categories that are descendants of $category
$entries = entry::search(
document: $category,
amount: 200,
depth: 100,
amount: 50,
categories_merge: 'name: v.name.@language',
/* products_merge: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language, brand: d.brand.@language, cost: d.cost.@currency})', */
parameters: ['language' => $this->language->name],
errors: $this->errors['catalog']
);
if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
// Pages
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
$category = $product = [];
// Write to the buffer of global variables of view templater
$this->view->categories = $search['categories'];
foreach ($entries as $entry) {
// Iterate over entries (descendands)
// Write entry to the buffer of entries (sort by $category and $product)
${$entry->_type}[] = $entry;
}
// Write to the buffer of global variables of view templater
$this->view->products = $search['products'];
$this->view->categories = $category;
// Write to the buffer of global variables of view templater
$this->view->products = $product;
if (isset($this->view->products) && count($this->view->products) > 0) {
// Amount of rendered products is more than 0
@ -230,51 +228,13 @@ final class catalog extends core
} else if (!isset($category)) {
// Not received identifier of the category
if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
// Pages
// Searching for root ascendants categories
$this->view->categories = entry::ascendants(
descendant: new category,
return: 'DISTINCT MERGE(d, { name: d.name.@language})',
parameters: ['language' => $this->language->name],
errors: $this->errors['catalog']
) ?? null;
} else if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'lists') {
// Lists
// Initializing the list of all categories
$categories = [];
// Initializing the structure of the list of all categories
$structure = [];
foreach (
entry::ascendants(
descendant: new category,
return: 'DISTINCT MERGE(d, { name: d.name.@language})',
parameters: ['language' => $this->language->name],
errors: $this->errors['catalog']
) ?? null as $document
) {
// Iterating over found root ascendants categories
// Generating the list (entering into recursion)
static::list(
document: $document,
language: $this->language,
categories: $categories,
structure: $structure,
errors: $this->errors['catalog']
);
}
// Writing the list of all categories into the templater variable
$this->view->categories = $categories;
// Writing the structure of the list of all categories into the templater variable
$this->view->structure = $structure;
}
// search for root ascendants categories
$this->view->categories = entry::ascendants(
descendant: new category,
return: 'DISTINCT MERGE(ascendant, { name: ascendant.name.@language})',
parameters: ['language' => $this->language->name],
errors: $this->errors['catalog']
) ?? null;
}
// Validating @todo add throwing errors
@ -333,12 +293,9 @@ final class catalog extends core
parameters: ['language' => $this->language->name],
errors: $this->errors['menu']
);
// Universalizing
if ($this->view->menu instanceof _document) $this->view->menu = [$this->view->menu];
}
if (str_contains($this->request->headers['accept'] ?? [], content::json->value)) {
if (str_contains($this->request->headers['accept'], content::json->value)) {
// Request for JSON response
// Initializing the response body buffer
@ -427,56 +384,4 @@ final class catalog extends core
// Exit (success/fail)
return null;
}
/**
* List
*
*
*
* @return void
*/
private static function list(_document $document, language $language, array &$categories = [], array &$structure = [], array &$errors = []): void
{
// Initializing the object
$category = new category;
if (method_exists($category, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of product document from ArangoDB to the implement object
$category->__document($document);
// Writing the category into the list of categories
$categories[$category->getId()] = $category;
// Writing the category into the structure of the list categories
$structure[$category->getId()] = [];
// Searching for entries that are descendants of $category
$search = model::search(
document: $category,
amount: 200,
categories_merge: 'name: v.name.@language',
parameters: ['language' => $language->name],
errors: $errors
);
if (count($search['categories']) > 0) {
// Found descendants categories
foreach ($search['categories'] as $document) {
// Iterating over descendants categories
// Entering into descendant level of the recursion for the list generation
static::list(
document: $document,
language: $language,
categories: $categories,
structure: $structure[$category->getId()],
errors: $errors
);
}
}
}
}
}

View File

@ -2,18 +2,18 @@
declare(strict_types=1);
namespace mirzaev\huesos\controllers;
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\huesos\views\templater,
mirzaev\huesos\models\core as models,
mirzaev\huesos\models\account,
mirzaev\huesos\models\session,
mirzaev\huesos\models\settings,
mirzaev\huesos\models\cart,
mirzaev\huesos\models\suspension,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency;
use mirzaev\arming_bot\views\templater,
mirzaev\arming_bot\models\core as models,
mirzaev\arming_bot\models\account,
mirzaev\arming_bot\models\session,
mirzaev\arming_bot\models\settings,
mirzaev\arming_bot\models\cart,
mirzaev\arming_bot\models\suspension,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Framework for PHP
use mirzaev\minimal\core as minimal,
@ -24,7 +24,7 @@ use mirzaev\minimal\core as minimal,
/**
* Core of controllers
*
* @package mirzaev\huesos\controllers
* @package mirzaev\arming_bot\controllers
*
* @param settings $settings Instance of the settings
* @param session $session Instance of the session
@ -167,7 +167,7 @@ class core extends controller
$this->account = $this->session->account($this->errors['account']);
// Initializing of the settings
$this->settings = settings::active(create: SETTINGS_PROJECT);
$this->settings = settings::active();
// Initializing of the language
$this->language = $this->account?->language ?? $this->session?->buffer['language'] ?? $this->settings?->language ?? language::en;

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,11 @@
declare(strict_types=1);
namespace mirzaev\huesos\controllers;
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\huesos\controllers\core,
mirzaev\huesos\models\menu;
use mirzaev\arming_bot\controllers\core,
mirzaev\arming_bot\models\menu;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content;
@ -14,7 +14,7 @@ use mirzaev\minimal\http\enumerations\content;
/**
* Controller of pages
*
* @package mirzaev\huesos\controllers
* @package mirzaev\arming_bot\controllers
* @param array $errors Registry of errors
*
* @method null offer() Public offer
@ -56,9 +56,6 @@ final class index extends core
parameters: ['language' => $this->language->name],
errors: $this->errors['menu']
);
// Universalizing
if ($this->view->menu instanceof _document) $this->view->menu = [$this->view->menu];
}
if (str_contains($this->request->headers['accept'], content::any->value)) {

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace mirzaev\huesos\controllers;
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\huesos\controllers\core,
mirzaev\huesos\models\cart as model,
mirzaev\huesos\models\product,
mirzaev\huesos\models\menu,
mirzaev\huesos\models\enumerations\language;
use mirzaev\arming_bot\controllers\core,
mirzaev\arming_bot\models\cart as model,
mirzaev\arming_bot\models\product,
mirzaev\arming_bot\models\menu,
mirzaev\arming_bot\models\enumerations\language;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content,
@ -18,7 +18,7 @@ use mirzaev\minimal\http\enumerations\content,
/**
* Controller of cart
*
* @package mirzaev\huesos\controllers
* @package mirzaev\arming_bot\controllers
*
* @param model|null $cart Instance of the cart
* @param array $errors Registry of errors
@ -74,9 +74,6 @@ final class cart extends core
parameters: ['language' => $this->language->name],
errors: $this->errors['menu']
);
// Universalizing
if ($this->view->menu instanceof _document) $this->view->menu = [$this->view->menu];
}
// Initializing the cart

View File

@ -2,11 +2,11 @@
declare(strict_types=1);
namespace mirzaev\huesos\controllers;
namespace mirzaev\arming_bot\controllers;
// Files of the project
use mirzaev\huesos\controllers\core,
mirzaev\huesos\models\account;
use mirzaev\arming_bot\controllers\core,
mirzaev\arming_bot\models\account;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content,
@ -18,7 +18,7 @@ use mirzaev\arangodb\document;
/**
* Controller of session
*
* @package mirzaev\huesos\controllers
* @package mirzaev\arming_bot\controllers
*
* @param array $errors Registry of errors
*
@ -53,7 +53,6 @@ final class session extends core
* @param ?string $auth_date
* @param ?string $hash
* @param ?string $query_id
* @param ?string $signature
*
* @return null
*/
@ -63,8 +62,7 @@ final class session extends core
?string $chat_type = null,
?string $auth_date = null,
?string $hash = null,
?string $query_id = null,
?string $signature = null
?string $query_id = null
): null {
if (str_contains($this->request->headers['accept'], content::json->value)) {
// Request for JSON response
@ -72,12 +70,6 @@ final class session extends core
// Declaring variables in the correct scope
$identifier = $domain = $language = null;
// Initializing data of the account
$data = json_decode($user);
// Initializing avatar of the account
$avatar = $data->photo_url;
if ($connected = isset($this->account)) {
// Found the account
@ -92,17 +84,10 @@ final class session extends core
} else {
// Not found the account
if (isset($user, $auth_date, $hash)) {
if (isset($user, $chat_instance, $chat_type, $auth_date, $hash)) {
// Received required parameters
$buffer = ['user' => $user];
if (isset($query_id)) $buffer += ['query_id' => $query_id];
if (isset($signature)) $buffer += ['signature' => $signature];
if (isset($chat_instance)) $buffer += ['chat_instance' => $chat_instance];
if (isset($chat_type)) $buffer += ['chat_type' => $chat_type];
$buffer += ['auth_date' => $auth_date];
$buffer = ['user' => $user, 'chat_instance' => $chat_instance, 'chat_type' => $chat_type, 'auth_date' => $auth_date];
ksort($buffer);
@ -124,6 +109,9 @@ final class session extends core
if (time() - $auth_date < 86400) {
// Authorization date less than 1 day ago
// Initializing data of the account
$data = json_decode($user);
// Initializing of the account
$account = account::initialize(
$data->id,
@ -158,9 +146,6 @@ final class session extends core
// Initializing domain of the account
$domain = $account->domain;
// Initializing avatar of the account
$avatar = $data->photo_url;
}
}
}
@ -176,7 +161,6 @@ final class session extends core
'connected' => (bool) $connected,
'identifier' => $identifier ?? null,
'domain' => $domain ?? null,
'avatar' => $avatar ?? null,
'language' => $language?->name ?? null,
'errors' => $this->errors
])

View File

@ -2,18 +2,18 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\status,
mirzaev\huesos\models\traits\buffer,
mirzaev\huesos\models\traits\cart,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\status,
mirzaev\arming_bot\models\traits\buffer,
mirzaev\arming_bot\models\traits\cart,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
@ -31,7 +31,7 @@ use exception;
/**
* Model of account
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -86,7 +86,7 @@ final class account extends core implements document_interface, collection_inter
if (method_exists($account, '__document')) {
// Object can implement a document from ArangoDB
// Implementinf parameters
// Abstractioning of parameters
if (isset($result->language)) $result->language = language::{$result->language};
if (isset($result->currency)) $result->currency = currency::{$result->currency};
@ -121,7 +121,7 @@ final class account extends core implements document_interface, collection_inter
'messages' => $registration->getCanReadAllGroupMessages()
],
'premium' => $registration->isPremium(),
'language' => language::{$registration->getLanguageCode() ?? 'en'}?->name ?? 'en',
'language' => language::{$registration->getLanguageCode()}->name ?? 'en',
'queries' => [
'inline' => $registration->getSupportsInlineQueries()
]
@ -145,7 +145,7 @@ final class account extends core implements document_interface, collection_inter
return static::initialize($identifier, errors: $errors);
} else throw new exception('Failed to register account');
} else throw new exception('Failed to find account');
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,17 +2,17 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\reservation,
mirzaev\huesos\models\traits\buffer,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\reservation,
mirzaev\arming_bot\models\traits\buffer,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
@ -22,14 +22,13 @@ use mirzaev\arangodb\collection,
use ArangoDBClient\Document as _document;
// Built-in libraries
use DateTime as datetime,
Exception as exception;
use exception;
/**
* Model of cart
*
* @uses reservation
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -111,9 +110,9 @@ final class cart extends core implements document_interface, collection_interfac
// Exit (success)
return isset($products['amount']) ? [$products['document']['_id'] => $products] : $products;
} else throw new exception('Failed to initialize ' . product::TYPE->name . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE->name . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -151,11 +150,11 @@ final class cart extends core implements document_interface, collection_interfac
// Search for all products in the cart
$result = @collection::execute(
<<<AQL
FOR v IN 1..1 INBOUND @cart GRAPH @graph
FILTER IS_SAME_COLLECTION(@collection, v._id)
COLLECT AGGREGATE amount = COUNT(v), cost = SUM(v.cost.@currency)
RETURN { amount, cost }
AQL,
FOR v IN 1..1 INBOUND @cart GRAPH @graph
FILTER IS_SAME_COLLECTION(@collection, v._id)
COLLECT AGGREGATE amount = COUNT(v), cost = SUM(v.cost.@currency)
RETURN { amount, cost }
AQL,
[
'graph' => 'catalog',
'cart' => $this->getId(),
@ -168,9 +167,9 @@ final class cart extends core implements document_interface, collection_interfac
// Exit (success)
return $result;
} else throw new exception('Failed to initialize ' . product::TYPE->name . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE->name . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -185,6 +184,7 @@ final class cart extends core implements document_interface, collection_interfac
return null;
}
/**
* Count
*
@ -222,9 +222,9 @@ final class cart extends core implements document_interface, collection_interfac
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . product::TYPE->name . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE->name . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -242,7 +242,7 @@ final class cart extends core implements document_interface, collection_interfac
/*
* Write
*
* Write the product into the cart
* Write the product in the cart
*
* @param product $product The product
* @param int $amount Amount of writings
@ -273,9 +273,9 @@ final class cart extends core implements document_interface, collection_interfac
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . product::TYPE->name . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE->name . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -324,9 +324,9 @@ final class cart extends core implements document_interface, collection_interfac
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . product::TYPE->name . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE->name . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -362,7 +362,7 @@ final class cart extends core implements document_interface, collection_interfac
// Exit (success)
return $this->share;
} else throw new exception('Failed to write confirmed cart to ArangoDB');
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -401,7 +401,7 @@ final class cart extends core implements document_interface, collection_interfac
// Exit (success)
return true;
} else throw new exception('Failed to write confirmed cart to ArangoDB');
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -416,263 +416,26 @@ final class cart extends core implements document_interface, collection_interfac
return false;
}
/**
* Account
*
* Search for the connected account
*
* @param array &$errors Registry of errors
*
* @return account|null The connected account, if found
*/
public function account(array &$errors = []): ?account
{
try {
if ($result = collection::execute(
<<<'AQL'
FOR v IN 1..1 OUTBOUND @cart GRAPH users
FILTER PARSE_IDENTIFIER(v._id).collection == @account
LIMIT 1
return v
AQL,
[
'cart' => $this->document->getId(),
'account' => account::COLLECTION
],
errors: $errors
)) {
// Found the instance of the account connected to the account
// Initializing the object
$account = new account;
if (method_exists($account, '__document')) {
// Object can implement a document from ArangoDB
// Implementinf parameters
if (isset($result->language)) $result->language = language::{$result->language};
if (isset($result->currency)) $result->currency = currency::{$result->currency};
// Writing the instance of the account document from ArangoDB to the implement object
$account->__document($result);
// Exit (success)
return $account;
}
}
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/**
* Cost
*
* Generate the cart products cost
*
* @param string &$list List of the calculated products
* @param array &$errors Registry of errors
*
* @return float|int|null The cart products cost, if generated
*/
public function cost(string &$list = '', array &$errors = []): float|int|null
{
try {
// Initializing the account
$account = $this->account($errors);
if ($account instanceof account) {
// Initialized the account
// Initializing products in the cart
$products = $this->products(language: $account->language ?? language::ru, currency: $account->currency ?? currency::rub);
if (!empty($products)) {
// Initialized products in the cart
// Declaring total cost of products
$cost = 0;
// Initializing iterator of rows
$row = 0;
foreach ($products as $product) {
// Iterating over products
// Generating formatted list of products for message
$list .= ++$row . '. ' . $product['document']['name'] . ' (' . $product['amount'] . 'шт)' . "\n";
// Generating total cost of products
$cost += $product['document']['cost'] * $product['amount'];
}
// Exit (success)
return $cost;
}
}
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
/*
* Order
*
* Create the order and connect to the cart and to the account
* Make the cart ordered (the account will create a new cart)
*
*
* @param array &$errors Registry of errors
*
* @return order|null The order, if created
*
* @todo Handling errors
* @return void
*/
public function order(array &$errors = []): ?order
public function order(array &$errors = []): void
{
try {
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
if (collection::initialize(order::COLLECTION, order::TYPE, errors: $errors)) {
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
if (collection::initialize(account::COLLECTION, account::TYPE, errors: $errors)) {
// Initialized collections
if (collection::initialize(reservation::COLLECTION, reservation::TYPE, errors: $errors)) {
if (collection::initialize(order::COLLECTION, order::TYPE, errors: $errors)) {
// Initialized collections
// Searching for the order
$existed = collection::execute(
<<<'AQL'
FOR v IN 1..1 OUTBOUND @cart GRAPH users
FILTER PARSE_IDENTIFIER(v._id).collection == @order
LIMIT 1
return v
AQL,
[
'cart' => $this->document->getId(),
'order' => order::COLLECTION
],
errors: $errors
);
if ($existed) {
// The order has already been created
// Initializing the object
$order = new order;
if (method_exists($order, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of the order document from ArangoDB to the implement object
$order->__document($existed);
// Exit (success)
return $order;
}
} else {
// The order has not been created
// Initializing a new order
$_id = document::write(
order::COLLECTION,
[
'active' => true,
/* 'term' => (int) new datetime('+15 minutes')->ormat('U') */
]
);
if ($result = collection::execute(
<<<'AQL'
FOR d IN @@collection
FILTER d._id == @_id && d.active == true
RETURN d
AQL,
[
'@collection' => order::COLLECTION,
'_id' => $_id
],
errors: $errors
)) {
// Found the instance of just created the new order
// Writing the ordered status for the cart
$this->document->ordered = true;
if (document::update($this->__document(), errors: $errors)) {
// Writed into ArangoDB
// Initializing the object
$order = new order;
if (method_exists($order, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of the order document from ArangoDB to the implement object
$order->__document($result);
// Connecting the cart to the order
$connected = $order->connect($this, $errors);
if ($connected) {
// Connected the cart with the order
if ($result = collection::execute(
<<<'AQL'
FOR v IN 1..1 OUTBOUND @cart GRAPH users
FILTER PARSE_IDENTIFIER(v._id).collection == @account
LIMIT 1
return v
AQL,
[
'cart' => $this->document->getId(),
'account' => account::COLLECTION
],
errors: $errors
)) {
// Found the instance of the account connected to the cart
// Initializing the object
$account = new account;
if (method_exists($account, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of the account document from ArangoDB to the implement object
$account->__document($result);
// Connecting the account with the order
$connected = $account->connect($order, $errors);
if ($connected) {
// Connected the account with the order
// Exit (success)
return $order;
}
}
}
}
} else throw new exception('Class ' . order::class . ' does not implement a document from ArangoDB');
} else throw new exception('Failed to write the ordered status for the cart');
} else throw new exception('Failed to create or find just created ' . static::class);
}
} else throw new exception('Failed to initialize ' . account::TYPE->name . ' collection: ' . account::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE->name . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . order::TYPE->name . ' collection: ' . order::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION);
} else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -682,8 +445,5 @@ final class cart extends core implements document_interface, collection_interfac
'stack' => $e->getTrace()
];
}
// Exit (fail)
return null;
}
}

View File

@ -2,17 +2,17 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\product,
mirzaev\huesos\models\category,
mirzaev\huesos\models\entry,
mirzaev\huesos\models\traits\files,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency,
mirzaev\huesos\models\traits\yandex\disk as yandex;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\product,
mirzaev\arming_bot\models\category,
mirzaev\arming_bot\models\entry,
mirzaev\arming_bot\models\traits\files,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency,
mirzaev\arming_bot\models\traits\yandex\disk as yandex;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content;
@ -33,7 +33,7 @@ use GdImage as image;
/**
* Model of the catalog
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -41,64 +41,7 @@ use GdImage as image;
final class catalog extends core
{
use yandex, files {
yandex::download as file;
yandex::list as folder;
}
/**
* Search
*
* @param category|product $document Ascendant document
* @param string|null $filter Expression for filtering (AQL)
* @param string|null $sort Expression for sorting (AQL)
* @param int $depth Amount of nodes for traversal search (subcategories/products inside subcategories...)
* @param int $page Page
* @param int $amount Amount of documents per page
* @param string|null $categories_merge Expression with paremeters to return for categories (AQL)
* @param string|null $products_merge Expression with paremeters to return for products (AQL)
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
* @param array &$errors Registry of errors
*
* @return array [categories => [document::class...], $products => [document::class...]]
*/
public static function search(
category|product $document,
?string $filter = 'v.deleted != true && v.hidden != true',
?string $sort = 'v.position ASC, v.created DESC',
int $depth = 1,
int $page = 1,
int $amount = 100,
?string $categories_merge = null,
?string $products_merge = null,
array $parameters = [],
array &$errors = []
): array {
// Search for entries that are descendants of $category
$entries = entry::search(
document: $document,
filter: $filter,
sort: $sort,
depth: $depth,
page: $page,
amount: $amount,
categories_merge: $categories_merge,
products_merge: $products_merge,
parameters: $parameters,
errors: $errors
);
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
$category = $product = [];
foreach ($entries as $entry) {
// Iterate over entries (descendands)
// Write entry to the buffer of entries (sort by $category and $product)
${$entry->_type}[] = $entry;
}
// Exit (success)
return ['categories' => $category, 'products' => $product];
yandex::download as yandex;
}
/**
@ -177,7 +120,7 @@ final class catalog extends core
// Iterate over categories
try {
if (!empty($row['identifier']) && is_int($row['identifier']) && $row['identifier'] > 0 && !empty($row['name'])) {
if (!empty($row['identifier']) && !empty($row['name'])) {
// Required cells are filled in
// Incrementing the counter of loaded categories
@ -252,10 +195,7 @@ final class catalog extends core
// Received images
// Initializing new images of the category
$images = static::folder(
uri: explode(' ', mb_trim($row['images']))[0],
errors: $errors
);
$images = explode(' ', mb_trim($row['images']));
// Reinitialize images? (true, if no images found or their amount does not match)
/* $reinitialize = !$category->images || count($category->images) !== count($images); */
@ -270,14 +210,14 @@ final class catalog extends core
// Initializing the buffer of images
$buffer = [];
foreach (is_array($images) ? $images : [] as $image) {
foreach ($images as $index => $file) {
// Iterating over new images
// Initializing identifier of the image
$identifier = preg_replace('/\.\w+$/', '', $image->name);
// Skipping empty URI`s
if (empty($file = mb_trim($file))) continue;
// Initializing path to directory of images in storage
$directory = DIRECTORY_SEPARATOR . 'categories' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $identifier;
$directory = DIRECTORY_SEPARATOR . 'categories' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $index;
// Initializing URL of the image in storage
$url = STORAGE . $directory;
@ -285,8 +225,8 @@ final class catalog extends core
// Initializing the directory in storage
if (!file_exists($url)) mkdir($url, 0775, true);
if ($downloaded = static::file(
uri: $image->public_url ?? $image->public_key,
if ($downloaded = static::yandex(
uri: $file,
destination: $url,
name: 'source',
errors: $errors
@ -309,11 +249,6 @@ final class catalog extends core
// Initializing implementator of the image
$boba = imagecreatefromjpeg($uri);
} else if ($downloaded['content'] === content::webp) {
// WEBP
// Initializing implementator of the image
$boba = imagecreatefromwebp($uri);
}
// Enabling better antialiasing
@ -345,10 +280,10 @@ final class catalog extends core
imagecopyresampled($biba, $boba, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
// Initializing URI of the resized image
$uri = $directory . DIRECTORY_SEPARATOR . "$resize.webp";
$uri = $directory . DIRECTORY_SEPARATOR . "$resize." . $downloaded['content']->extension();
// Saving the image
imagewebp($biba, STORAGE . $uri);
imagePng($biba, STORAGE . $uri);
// Writing the resized image to the buffer of resized images
$resized[$resize] = $uri;
@ -356,7 +291,7 @@ final class catalog extends core
// Writing the image to the buffer if images
$buffer[] = [
'source' => $image->public_url ?? null,
'source' => $file,
'storage' => [
'source' => $directory . DIRECTORY_SEPARATOR . $downloaded['name'] . '.' . $downloaded['content']->extension(),
] + $resized
@ -414,7 +349,7 @@ final class catalog extends core
// Iterate over products
try {
if (!empty($row['identifier']) && is_int($row['identifier']) && $row['identifier'] > 0 && !empty($row['name'])) {
if (!empty($row['identifier']) && !empty($row['name'])) {
// Required cells are filled in
// Incrementing the counter of loaded products
@ -450,7 +385,7 @@ final class catalog extends core
// Initializing position of the product
if (empty($product->position) || $product->position !== $row['position'])
$product->position = isset($row['position']) ? (int) $row['position'] : 0;
$product->position = $row['position'];
} else {
// Not initialized the product
@ -464,7 +399,7 @@ final class catalog extends core
dimensions: ['x' => $row['x'], 'y' => $row['y'], 'z' => $row['z']],
brand: [$language->name => $row['brand']],
compatibility: [$language->name => $row['compatibility']],
position: isset($row['position']) ? (int) $row['position'] : 0,
position: (int) $row['position'] ?? null,
errors: $errors
);
@ -501,11 +436,8 @@ final class catalog extends core
if (!empty($row['images'])) {
// Received images
// Initializing new images of the product
$images = static::folder(
uri: explode(' ', mb_trim($row['images']))[0],
errors: $errors
);
// Initializing new images of the category
$images = explode(' ', mb_trim($row['images']));
// Reinitialize images? (true, if no images found or their amount does not match)
/* $reinitialize = !$product->images || count($product->images) !== count($images); */
@ -520,14 +452,14 @@ final class catalog extends core
// Initializing the buffer of images
$buffer = [];
foreach (is_array($images) ? $images : [] as $image) {
foreach ($images as $index => $file) {
// Iterating over new images
// Initializing identifier of the image
$identifier = preg_replace('/\.\w+$/', '', $image->name);
// Skipping empty URI`s
if (empty($file = mb_trim($file))) continue;
// Initializing path to directory of images in storage
$directory = DIRECTORY_SEPARATOR . 'products' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $identifier;
$directory = DIRECTORY_SEPARATOR . 'products' . DIRECTORY_SEPARATOR . $row['identifier'] . DIRECTORY_SEPARATOR . $index;
// Initializing URL of the image in storage
$url = STORAGE . $directory;
@ -535,8 +467,8 @@ final class catalog extends core
// Initializing the directory in storage
if (!file_exists($url)) mkdir($url, 0775, true);
if ($downloaded = static::file(
uri: $image->public_url ?? $image->public_key,
if ($downloaded = static::yandex(
uri: $file,
destination: $url,
name: 'source',
errors: $errors
@ -559,11 +491,6 @@ final class catalog extends core
// Initializing implementator of the image
$boba = imagecreatefromjpeg($uri);
} else if ($downloaded['content'] === content::webp) {
// WEBP
// Initializing implementator of the image
$boba = imagecreatefromwebp($uri);
}
// Enabling better antialiasing
@ -595,10 +522,10 @@ final class catalog extends core
imagecopyresampled($biba, $boba, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
// Initializing URI of the resized image
$uri = $directory . DIRECTORY_SEPARATOR . "$resize.webp";
$uri = $directory . DIRECTORY_SEPARATOR . "$resize." . $downloaded['content']->extension();
// Saving the image
imagewebp($biba, STORAGE . $uri);
imagePng($biba, STORAGE . $uri);
// Writing the resized image to the buffer of resized images
$resized[$resize] = $uri;
@ -606,7 +533,7 @@ final class catalog extends core
// Writing the image to the buffer if images
$buffer[] = [
'source' => $image->public_url ?? null,
'source' => $file,
'storage' => [
'source' => $directory . DIRECTORY_SEPARATOR . $downloaded['name'] . '.' . $downloaded['content']->extension(),
] + $resized

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
@ -21,7 +21,7 @@ use exception;
/**
* Model of category
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -69,7 +69,7 @@ final class category extends core implements document_interface, collection_inte
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Framework for ArangoDB
use mirzaev\arangodb\enumerations\collection\type;
@ -16,7 +16,7 @@ use mirzaev\arangodb\enumerations\collection\type;
/**
* Model of connect
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Framework for PHP
use mirzaev\minimal\model;
@ -20,7 +20,7 @@ use exception;
/**
* Core of models
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -127,7 +127,7 @@ class core extends model
// Exit (success)
return $result;
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to registry of errors
$errors[] = [

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\deliveries;
namespace mirzaev\arming_bot\models\deliveries;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// The HTTP PSR-18 adapter for Guzzle HTTP client
use Http\Adapter\Guzzle7\Client as guzzle;
@ -30,7 +30,7 @@ use exception,
/**
* Model of CDEK
*
* @package mirzaev\huesos\models\deliveries
* @package mirzaev\arming_bot\models\deliveries
*
* @method cities|null location(string $name, array &$errors) Search for CDEK location by name
*

View File

@ -2,11 +2,11 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\enumerations\delivery as company;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\enumerations\delivery as company;
// Built-in libraries
use exception;
@ -14,7 +14,7 @@ use exception;
/**
* Model of settings
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Library for ArangoDB
use ArangoDBClient\Document as _document;
@ -24,7 +24,7 @@ use exception;
/**
* Model of entry
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -73,9 +73,9 @@ final class entry extends core implements document_interface, collection_interfa
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $to::TYPE->name . ' collection: ' . $to::COLLECTION);
} else throw new exception('Failed to initialize ' . $from::TYPE->name . ' collection: ' . $from::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $to::TYPE . ' collection: ' . $to::COLLECTION);
} else throw new exception('Failed to initialize ' . $from::TYPE . ' collection: ' . $from::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -111,21 +111,16 @@ final class entry extends core implements document_interface, collection_interfa
try {
if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) {
// Initialized the collection
// Search for ascendants
// Search for ascendants
if ($result = collection::execute(
sprintf(
<<<'AQL'
let from = (
FOR e IN @@edge
RETURN DISTINCT e._from
)
FOR d in @@collection
FILTER !POSITION(from, d._id)
RETURN %s
FOR d IN @@collection
FOR ascendant IN OUTBOUND d @@edge
RETURN %s
AQL,
empty($return) ? 'DISTINCT d' : $return
empty($return) ? 'DISTINCT ascendant' : $return
),
[
'@collection' => $descendant::COLLECTION,
@ -138,7 +133,7 @@ final class entry extends core implements document_interface, collection_interfa
// Exit (success)
return is_array($result) ? $result : [$result];
} else return [];
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -194,9 +189,9 @@ final class entry extends core implements document_interface, collection_interfa
// Exit (success)
return is_array($entry) ? $entry[0] : $entry;
} else return null;
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $to::TYPE->name . ' collection: ' . $to::COLLECTION);
} else throw new exception('Failed to initialize ' . $from::TYPE->name . ' collection: ' . $from::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $to::TYPE . ' collection: ' . $to::COLLECTION);
} else throw new exception('Failed to initialize ' . $from::TYPE . ' collection: ' . $from::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -223,9 +218,8 @@ final class entry extends core implements document_interface, collection_interfa
* @param category|product $document Ascendant document
* @param string|null $filter Expression for filtering (AQL)
* @param string|null $sort Expression for sorting (AQL)
* @param int $depth Amount of nodes for traversal search (subcategories/products inside subcategories...)
* @param int $page Page
* @param int $amount Amount of documents per page
* @param int $page Страница
* @param int $amount Количество товаров на странице
* @param string|null $categories_merge Expression with paremeters to return for categories (AQL)
* @param string|null $products_merge Expression with paremeters to return for products (AQL)
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
@ -237,7 +231,6 @@ final class entry extends core implements document_interface, collection_interfa
category|product $document,
?string $filter = 'v.deleted != true && v.hidden != true',
?string $sort = 'v.position ASC, v.created DESC',
int $depth = 1,
int $page = 1,
int $amount = 100,
?string $categories_merge = null,
@ -254,13 +247,12 @@ final class entry extends core implements document_interface, collection_interfa
return is_array($result = collection::execute(
sprintf(
<<<'AQL'
FOR v IN 1..%u INBOUND @document GRAPH @graph
FOR v IN 1..1 INBOUND @document GRAPH @graph
%s
%s
LIMIT @offset, @amount
RETURN DISTINCT IS_SAME_COLLECTION(@category, v._id) ? MERGE(v, {_type: @category%s}) : MERGE(v, {_type: @product%s})
AQL,
$depth,
empty($filter) ? '' : "FILTER $filter",
empty($sort) ? '' : "SORT $sort",
empty($categories_merge) ? '' : ", $categories_merge",
@ -276,8 +268,8 @@ final class entry extends core implements document_interface, collection_interfa
] + $parameters,
errors: $errors
)) ? $result : [$result];
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $document::TYPE->name . ' collection: ' . $document::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -323,8 +315,8 @@ final class entry extends core implements document_interface, collection_interfa
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $document::TYPE->name . ' collection: ' . $document::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,15 +2,15 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\enumerations;
namespace mirzaev\arming_bot\models\enumerations;
// Files of the project
use mirzaev\huesos\models\enumerations\language;
use mirzaev\arming_bot\models\enumerations\language;
/**
* Types of currencies by ISO 4217 standart
*
* @package mirzaev\huesos\models\enumerations
* @package mirzaev\arming_bot\models\enumerations
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\enumerations;
namespace mirzaev\arming_bot\models\enumerations;
/**
* Types of deliveries
*
* @package mirzaev\huesos\models\enumerations
* @package mirzaev\arming_bot\models\enumerations
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\enumerations;
namespace mirzaev\arming_bot\models\enumerations;
/**
* Types of languages by ISO 639-1 standart
*
* @package mirzaev\huesos\models\enumerations
* @package mirzaev\arming_bot\models\enumerations
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\enumerations;
namespace mirzaev\arming_bot\models\enumerations;
/**
* Types of session verification
*
* @package mirzaev\huesos\models\enumerations
* @package mirzaev\arming_bot\models\enumerations
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\interfaces;
namespace mirzaev\arming_bot\models\interfaces;
// Framework for ArangoDB
use mirzaev\arangodb\enumerations\collection\type;
@ -10,7 +10,7 @@ use mirzaev\arangodb\enumerations\collection\type;
/**
* Interface for implementing a collection from ArangoDB
*
* @package mirzaev\huesos\models\traits
* @package mirzaev\arming_bot\models\traits
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\interfaces;
namespace mirzaev\arming_bot\models\interfaces;
// Library для ArangoDB
use ArangoDBClient\Document as _document;
@ -12,7 +12,7 @@ use ArangoDBClient\Document as _document;
*
* @param _document $document An instance of the ArangoDB document from ArangoDB (protected readonly)
*
* @package mirzaev\huesos\models\traits
* @package mirzaev\arming_bot\models\traits
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,18 +2,18 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
/**
* Model of menu
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\reservation,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Library for ArangoDB
use ArangoDBClient\Document as _document;
// Built-in libraries
use exception;
/**
* Model of order
*
* @uses !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class order extends core implements document_interface, collection_interface
{
use document_trait;
/**
* Name of the collection in ArangoDB
*/
final public const string COLLECTION = 'order';
}

View File

@ -2,15 +2,15 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
@ -25,7 +25,7 @@ use exception;
/**
* Model of a product
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -100,7 +100,7 @@ final class product extends core implements document_interface, collection_inter
] + $data,
errors: $errors
);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -225,7 +225,7 @@ final class product extends core implements document_interface, collection_inter
// Exit (success)
return is_array($result) ? $result : [$result];
}
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -281,7 +281,7 @@ final class product extends core implements document_interface, collection_inter
// Exit (success)
return is_array($result) ? $result : [$result];
} else return [];
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\cart,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\cart,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface;
// Framework for ArangoDB
use mirzaev\arangodb\enumerations\collection\type;
@ -18,7 +18,7 @@ use mirzaev\arangodb\enumerations\collection\type;
* Model of reservtion
*
* @used-by cart
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,20 +2,20 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\account,
mirzaev\huesos\models\connect,
mirzaev\huesos\models\enumerations\session as verification,
mirzaev\huesos\models\traits\status,
mirzaev\huesos\models\traits\buffer,
mirzaev\huesos\models\traits\cart,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency;
use mirzaev\arming_bot\models\account,
mirzaev\arming_bot\models\connect,
mirzaev\arming_bot\models\enumerations\session as verification,
mirzaev\arming_bot\models\traits\status,
mirzaev\arming_bot\models\traits\buffer,
mirzaev\arming_bot\models\traits\cart,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
@ -30,7 +30,7 @@ use exception;
/**
* Model of a session
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -114,14 +114,14 @@ final class session extends core implements document_interface, collection_inter
$session->hash = sodium_bin2hex(sodium_crypto_generichash($_id));
if (document::update($session, errors: $errors)) {
// Writed into ArangoDB
// Writed to ArangoDB
// Writing instance of the session document from ArangoDB to the property of the implementing object
$this->__document($session);
} else throw new exception('Failed to write the session data');
} else throw new exception('Failed to create or find just created session');
}
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -185,9 +185,9 @@ final class session extends core implements document_interface, collection_inter
return $account;
} else throw new exception('Class ' . account::class . ' does not implement a document from ArangoDB');
} else return null;
} else throw new exception('Failed to initialize ' . account::TYPE->name . ' collection: ' . account::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE->name . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . account::TYPE . ' collection: ' . account::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -232,7 +232,7 @@ final class session extends core implements document_interface, collection_inter
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
@ -279,7 +279,7 @@ final class session extends core implements document_interface, collection_inter
],
errors: $errors
);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,15 +2,15 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
@ -25,7 +25,7 @@ use exception;
/**
* Model of settings
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -96,7 +96,7 @@ final class settings extends core implements document_interface, collection_inte
// Re-search (without creating) and exit (success || fail)
return static::active(errors: $errors);
} else throw new exception('Active settings not found');
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace mirzaev\huesos\models;
namespace mirzaev\arming_bot\models;
// Files of the project
use mirzaev\huesos\models\core,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\enumerations\language;
use mirzaev\arming_bot\models\core,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language;
// Framework for ArangoDB
use mirzaev\arangodb\collection,
@ -25,7 +25,7 @@ use exception,
/**
* Model of a suspension
*
* @package mirzaev\huesos\models
* @package mirzaev\arming_bot\models
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -84,7 +84,7 @@ final class suspension extends core implements document_interface, collection_in
return $suspension;
} else throw new exception('Class ' . static::class . ' does not implement a document from ArangoDB');
} else return null;
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\traits;
namespace mirzaev\arming_bot\models\traits;
// Files of the project
use mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\enumerations\currency;
use mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\enumerations\language,
mirzaev\arming_bot\models\enumerations\currency;
// Library for ArangoDB
use ArangoDBClient\Document as _document;
@ -28,7 +28,7 @@ use exception;
* @param static TYPE Type of the collection in ArangoDB
*
* @uses collection_interface
* @package mirzaev\huesos\models\traits
* @package mirzaev\arming_bot\models\traits
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -64,7 +64,7 @@ trait buffer
// Writing to ArangoDB and exit (success)
return document::update($this->__document(), errors: $errors);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\traits;
namespace mirzaev\arming_bot\models\traits;
// Files of the project
use mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\traits\document as document_trait,
mirzaev\huesos\models\connect,
mirzaev\huesos\models\cart as model;
use mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\traits\document as document_trait,
mirzaev\arming_bot\models\connect,
mirzaev\arming_bot\models\cart as model;
// Library для ArangoDB
use ArangoDBClient\Document as _document;
@ -28,7 +28,7 @@ use exception;
*
* @uses collection_interface
* @uses document_interface
* @package mirzaev\huesos\models\traits
* @package mirzaev\arming_bot\models\traits
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -132,9 +132,9 @@ trait cart
} else throw new exception('Class ' . model::class . ' does not implement a document from ArangoDB');
} else throw new exception('Failed to create or find just created ' . static::class);
}
} else throw new exception('Failed to initialize ' . model::TYPE->name . ' collection: ' . model::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE->name . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . model::TYPE . ' collection: ' . model::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\traits;
namespace mirzaev\arming_bot\models\traits;
// Files of the project
use mirzaev\huesos\models\interfaces\document as document_interface,
mirzaev\huesos\models\interfaces\collection as collection_interface,
mirzaev\huesos\models\connect;
use mirzaev\arming_bot\models\interfaces\document as document_interface,
mirzaev\arming_bot\models\interfaces\collection as collection_interface,
mirzaev\arming_bot\models\connect;
// Library для ArangoDB
use ArangoDBClient\Document as _document;
@ -26,7 +26,7 @@ use exception;
* @var protected readonly _document|null $document An instance of the ArangoDB document
*
* @uses document_interface
* @package mirzaev\huesos\models\traits
* @package mirzaev\arming_bot\models\traits
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -79,12 +79,10 @@ trait document
/**
* Connect
*
* Searches for a connection document, otherwise creates one
*
* @param collecton_interface $document Document
* @param array &$errors Registry of errors
*
* @return string|null The identifier of the "connect" edge collection, if created or found
* @return string|null The identifier of the created edge of the "connect" collection, if created
*/
public function connect(collection_interface $document, array &$errors = []): ?string
{
@ -97,50 +95,19 @@ trait document
if ($this->document instanceof _document) {
// Initialized instance of the document from ArangoDB
// Searching for a connection
$found = collection::execute(
<<<'AQL'
FOR d IN @@collection
FILTER d._from == @_from && d._to == @_to && d.active == true
RETURN d
AQL,
// Writing document and exit (success)
return framework_document::write(
connect::COLLECTION,
[
'@collection' => connect::COLLECTION,
'_from' => $document->getId(),
'_to' => $this->document->getId()
],
errors: $errors
);
if ($found) {
// Found the connection document
// Exit (success)
return $found->getId();
} else {
// Not found the connection document
// Creting the connection document
$created = framework_document::write(
connect::COLLECTION,
[
'_from' => $document->getId(),
'_to' => $this->document->getId()
],
errors: $errors
);
if ($created) {
// Created the connection document
// Exit (success)
return $created;
}
}
} else throw new exception('The instance of the document from ArangoDB is not initialized');
} else throw new exception('Failed to initialize ' . $document::TYPE->name . ' collection: ' . $document::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE->name . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE->name . ' collection: ' . static::COLLECTION);
} else throw new exception('Failed to initialize ' . $document::TYPE . ' collection: ' . $document::COLLECTION);
} else throw new exception('Failed to initialize ' . connect::TYPE . ' collection: ' . connect::COLLECTION);
} else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION);
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\traits;
namespace mirzaev\arming_bot\models\traits;
// Built-in libraries
use exception;
@ -10,7 +10,7 @@ use exception;
/**
* Trait for initialization of files handlers
*
* @package mirzaev\huesos\models\traits
* @package mirzaev\arming_bot\models\traits
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\traits;
namespace mirzaev\arming_bot\models\traits;
// Built-in libraries
use exception;
@ -10,7 +10,7 @@ use exception;
/**
* Trait for initialization of a status
*
* @package mirzaev\huesos\models\traits
* @package mirzaev\arming_bot\models\traits
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace mirzaev\huesos\models\traits\yandex;
namespace mirzaev\arming_bot\models\traits\yandex;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content;
@ -13,7 +13,7 @@ use exception;
/**
* Trait for "Yandex Disk"
*
* @package mirzaev\huesos\models\traits\yandex
* @package mirzaev\arming_bot\models\traits\yandex
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
@ -21,14 +21,14 @@ use exception;
trait disk
{
/**
* Download the file from "Yandex Disk"
* Download file from "Yandex Disk"
*
* @param string $uri URI of the file from "Yandex Disk"
* @param string $destination Destination to write the file
* @param string|int $name Name for the file
* @param array &$errors Registry of errors
*
* @return array|false The [destination, name, content] array of the downloaded file, if the file was downloaded
* @return array|false The [destination, name, content] of the downloaded file, if the file was downloaded
*/
private static function download(
string $uri,
@ -101,52 +101,4 @@ trait disk
// Exit (fail)
return false;
}
/**
* Initialize list of files inside the folder from "Yandex Disk"
*
* @param string $uri URI of the folder from "Yandex Disk"
* @param array &$errors Registry of errors
*
* @return array|false JSON objects list of files in the folder
*/
private static function list(string $uri, array &$errors = []): array|false
{
try {
if (!empty($uri)) {
// Not empty URI
// Initializing URL of the file
$url = "https://cloud-api.yandex.net/v1/disk/public/resources?public_key=$uri";
// Checking if the folder is available
$session = curl_init($url);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_exec($session);
$code = curl_getinfo($session, CURLINFO_RESPONSE_CODE);
curl_close($session);
if ($code === 200) {
// The folder is available
// Downloading the list of files in the folder
$files = json_decode(file_get_contents($url));
// Exit (success)
return isset($files?->_embedded?->items) ? $files->_embedded->items : [$files];
} else throw new exception("Failed to download list of files inside the folder by link: $uri");
} else throw new exception("Empty URI");
} catch (exception $e) {
// Writing to the registry of errors
$errors[] = [
'text' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'stack' => $e->getTrace()
];
}
// Exit (fail)
return false;
}
}

View File

@ -2,31 +2,28 @@
declare(strict_types=1);
namespace mirzaev\huesos;
namespace mirzaev\arming_bot;
// Framework for PHP
use mirzaev\minimal\core,
mirzaev\minimal\route;
// Enabling debugging
/* ini_set('error_reporting', E_ALL);
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); */
ini_set('display_startup_errors', 1);
// Версия робота
define('ROBOT_VERSION', '1.0.0');
define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
define('STORAGE', realpath('..' . DIRECTORY_SEPARATOR . 'storage'));
define('SETTINGS', realpath('..' . DIRECTORY_SEPARATOR . 'settings'));
define('SETTINGS_PROJECT', require(SETTINGS . DIRECTORY_SEPARATOR . 'project.php'));
define('INDEX', __DIR__);
define('ROOT', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
define('THEME', 'default');
define('CDEK', require(SETTINGS . DIRECTORY_SEPARATOR . 'deliveries' . DIRECTORY_SEPARATOR . 'cdek.php'));
define('TELEGRAM_KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php'));
// Initialize dependencies
require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
@ -40,12 +37,9 @@ $core->router
->write('/cart', new route('cart', 'index', 'cart'), 'GET')
->write('/cart/product', new route('cart', 'product', 'cart'), 'PATCH')
->write('/cart/summary', new route('cart', 'summary', 'cart'), 'GET')
/* ->write('/cart/share', new route('cart', 'share', 'cart'), 'POST') */
->write('/cart/share', new route('cart', 'share', 'cart'), 'POST')
->write('/cart/attach', new route('cart', 'attach', 'cart'), 'POST')
->write('/order/robokassa', new route('cart', 'robokassa', 'cart'), 'GET')
->write('/api/robokassa/result', new route('api\acquirings\robokassa', 'result'), 'POST')
->write('/robokassa/success', new route('api\acquirings\robokassa', 'success'), 'GET')
->write('/robokassa/fail', new route('api\acquirings\robokassa', 'fail'), 'GET')
->write('/account/write', new route('account', 'write', 'account'), 'PATCH')
->write('/session/write', new route('session', 'write', 'session'), 'PATCH')
->write('/session/connect/telegram', new route('session', 'telegram', 'session'), 'PUT')

View File

@ -17,11 +17,11 @@ class core {
// Window
static window;
// Account
static account;
// The "loading" element
static loading = document.getElementById("loading");
static status_loading = document.getElementById("loading");
// The "account" element
static status_account = document.getElementById("account");
// The <header> element
static header = document.body.getElementsByTagName("header")[0];

View File

@ -30,10 +30,10 @@ export default class account {
* @return {void}
*/
static authentication() {
core.loading.removeAttribute("disabled");
core.status_loading.removeAttribute("disabled");
const timer_for_response = setTimeout(() => {
core.loading.setAttribute("disabled", true);
core.status_loading.setAttribute("disabled", true);
}, 200);
core.modules.connect("telegram").then(() => {
@ -44,7 +44,7 @@ export default class account {
.request(
"/session/connect/telegram",
core.telegram.api.initData,
"PUT",
'PUT'
)
.then((json) => {
if (json) {
@ -63,66 +63,20 @@ export default class account {
// Success (not received errors)
if (json.connected === true) {
// Deactivating the loading screen
core.loading.setAttribute("disabled", true);
core.status_loading.setAttribute("disabled", true);
clearTimeout(timer_for_response);
core.account = {
identifier: json.identifier
};
// Initializing the account element
const account = document.getElementById("account");
if (account instanceof HTMLElement) {
// Initialized the account element
// Initializing the account link
const a = account.getElementsByTagName("a")[0];
if (a instanceof HTMLElement) {
// Initialized the account link
a.setAttribute("onclick", "core.account.profile()");
a.innerText = json.domain.length > 0
? "@" + json.domain
: "ERROR";
} else {
// Not initialized the account link
if (json.avatar) {
// Received the avatar image
// Initialize the menu button icon
const icon = account.getElementsByTagName("i")[0];
if (icon instanceof HTMLElement) {
// Initialized the menu button icon
setTimeout(function () {
// Hiding the menu button icon
icon.classList.add("hidden");
}, 3000);
}
// Initializing the avatar image element
const image = document.createElement("img");
image.setAttribute("src", json.avatar);
image.style.setProperty("opacity", "0");
image.style.setProperty("--animation-delay", "2s");
image.classList.add("opacity", "animated");
// Writing the avatar image element
account.appendChild(image);
}
}
}
const a = core.status_account.getElementsByTagName("a")[0];
a.setAttribute("onclick", "core.account.profile()");
a.innerText = json.domain.length > 0
? "@" + json.domain
: "ERROR";
}
if (
json.language !== null &&
typeof json.language === "string" &&
json.language.length === 2
json.langiage.length === 2
) {
core.language = json.language;
}

View File

@ -69,7 +69,7 @@ export default class cart {
* @name Write (interface)
*
* @description
* Write the product into the cart
* Write the product in the cart
*
* @param {HTMLButtonElement|HTMLInputElement|null} element Handler elememnt of the product
* @param {HTMLElement} product The product element
@ -388,7 +388,7 @@ export default class cart {
static async share(button) {
return await core.modules.connect("telegram").then(
() => {
// Imported the telegram module
// Imported the damper module
// Disabling button
button?.setAttribute("disabled", true);
@ -771,24 +771,6 @@ Object.assign(
} else {
// Success (not received errors)
if (json.amount > 0) {
// The cart has products
// Writing the CSS variable of the document element
document.documentElement.style.setProperty(
"--cart-amount",
'"' + json.amount + '"',
);
} else {
// The cart has no products
// Writing the CSS variable of the document element
document.documentElement.style.setProperty(
"--cart-amount",
"unset",
);
}
// Initializing the summary amount <span> element
const amount = document.getElementById("amount");
@ -862,7 +844,7 @@ Object.assign(
try {
// Request
return await core.request("/cart/share", undefined, "POST")
.then(async (json) => {
.then((json) => {
if (json) {
// Received a JSON-response
@ -881,43 +863,13 @@ Object.assign(
if (json.share) {
// Received sharing hash
// Sending the request to the chat-robot
const sended = core.telegram.api.sendData(
// Request to the chat-robot
core.telegram.api.sendData(
JSON.stringify({
type: "cart_share",
hash: json.share,
}),
);
if (sended === undefined) {
// Failed to send the request
if (core.account.identifier > 0) {
// Initialized the account identifier
// Request
return await core.request(
"/cart/attach",
'share=' + json.share,
"POST",
).then((json) => {
if (json) {
// Received a JSON-response
if (json.success) {
// Received the success status
core.modules.connect("telegram").then(() => {
// Imported the telegram module
// Closing the Telegram Mini App
core.telegram.api.close();
});
}
}
});
}
}
}
/* if (json.robokassa) {

View File

@ -15,11 +15,6 @@ export default class loader {
*/
static type = "module";
/**
* @name Actial URI
*/
static uri;
/**
* @name Load
*
@ -28,7 +23,7 @@ export default class loader {
*
* @return {Promise}
*/
static async load(uri = "/", body, back = false) {
static async load(uri = "/", body) {
if (typeof uri === "string") {
// Received and validated uri
@ -59,27 +54,8 @@ export default class loader {
} else {
// Success (not received errors)
if (back) {
// Requested to go back
// Writing actual URI
this.uri = history.state?.previous;
// Deletimg from the browser history
history.back();
} else {
// Requested to go forward
// Writing to the browser history
history.pushState(
{ previous: this.uri },
json.title ?? uri,
uri,
);
// Writing actual URI
this.uri = uri;
}
// Writing to the browser history
history.pushState({}, json.title ?? uri, uri);
/**
* The <title>
@ -246,10 +222,7 @@ export default class loader {
// Found the last <section> element inside the <body> element
// Writing the <main> element after the last <section> element inside the <body> element
body.insertBefore(
core.main,
section.nextElementSibling,
);
body.insertBefore(core.main, section.nextElementSibling);
} else {
// Not found section elements <section> inside the <body> element

View File

@ -21,11 +21,6 @@ export default class telegram {
* @see {@link https://core.telegram.org/bots/webapps#initializing-mini-apps}
*/
static api = window.Telegram.WebApp;
/**
* @name List of BackButton events
*/
static back = [];
}
// Connecting to the core

View File

@ -2,10 +2,10 @@
core.modules.connect(["telegram"])
.then(() => {
// Imported the telegram module
//
// Expanding the "Web App" window
core.telegram.api.expand();
// core.telegram.api.expand();
// Writing settings for the "Web App" window
core.telegram.api.enableVerticalSwipes();
@ -24,20 +24,6 @@ core.modules.connect(["telegram"])
document.documentElement.style.setProperty('--tg-theme-section-bg-color', 'var(--tg-theme-secondary-bg-color)');
}
if (core.telegram.back.length > 0) {
// Initialized BackButton events
// Initializing the "Back Button" of the "Web App" window
core.telegram.api.BackButton.show();
for (const event of core.telegram.back) {
// Iterating over BackButton events
// Initializing the BackButton event listener
core.telegram.api.BackButton.onClick(event);
}
}
core.modules.connect(["session", "account"])
.then(() => {
//

View File

@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace mirzaev\arming_bot;
// Files of the project
use mirzaev\arming_bot\controllers\core as controller,
mirzaev\arming_bot\models\core as model,
mirzaev\arming_bot\models\cart,
mirzaev\arming_bot\models\telegram;
// Framework for Telegram
use Zanzara\Zanzara,
Zanzara\Context,
Zanzara\Config;
// Framework for ArangoDB
use mirzaev\arangodb\document;
ini_set('error_reporting', E_ALL ^ E_DEPRECATED);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// Версия робота
define('ROBOT_VERSION', '1.0.0');
// Путь до настроек
define('SETTINGS', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'settings');
// Путь до хранилища
define('STORAGE', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'storage');
// Файл в формате xlsx с примером excel-документа для импорта каталога
define('CATALOG_EXAMPLE', STORAGE . DIRECTORY_SEPARATOR . 'example.xlsx');
// Файл в формате xlsx для импорта каталога
define('CATALOG_IMPORT', STORAGE . DIRECTORY_SEPARATOR . 'import.xlsx');
/**
* Ключ чат-робота Telegram
* @deprecated
*/
define('KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php'));
define('TELEGRAM_KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php'));
// Initialize dependencies
require __DIR__ . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR
. 'vendor' . DIRECTORY_SEPARATOR
. 'autoload.php';
// Инициализация ядра контроллеров MINIMAL
/* new controller(new core, false); */
// Инициализация ядра моделей MINIMAL
new model(true);
$config = new Config();
$config->setParseMode(Config::PARSE_MODE_MARKDOWN);
$config->useReactFileSystem(true);
$bot = new Zanzara(TELEGRAM_KEY, $config);
$bot->onUpdate(function (Context $ctx): void {
// Initializing the message
$message = $ctx->getMessage();
// Initializing the "web app" data
$app = $message?->getWebAppData();
if (!empty($app)) {
// Initialized the "web app" data
// Initializing request from "web app" data
$request = json_decode($app->getData(), false, 10);
if ($request->type === 'cart_share') {
// Cart attaching
// Attaching cart to the Telegram account
telegram::cart_attach($ctx, $request->hash);
}
} else {
// Not initialized the "web app" data
// Initializing account
$account = $ctx->get('account');
if ($account) {
// Initialized the account
if (!empty($message)) {
// Initialized the message
// Initializing the contact data
$contact = $message?->getContact();
if (!empty($contact)) {
// Initialized the contact data
// Sanitizing received SIM-number (only numbers)
$sanitized = preg_replace('/[^\d]/', '', $contact->getPhoneNumber());
if (!empty($sanitized)) {
// Sanitized receiver SIM-number
// Writing receiver SIM-number into the account
$account->receiver = ['sim' => (int) $sanitized] + ($account->receiver ?? []);
// Deabstracting the language parameter
$account->language = $account->language->name;
if (document::update($account->__document())) {
// Writed the account instance into the ArangoDB document
$ctx->sendMessage(
<<<TXT
*SIM\-номер зарегистрирован:* $sanitized
TXT,
[
'reply_markup' => [
'remove_keyboard' => true
]
]
)->then(function ($message) use ($ctx) {
// Sended message
// Sending the account parameters menu
telegram::account_parameters($ctx);
});
} else {
// Not writed the account instance into the ArangoDB document
// Sending the message
$ctx->sendMessage('⚠️ *Не удалось записать SIM\-номер*');
}
}
}
}
}
}
unset($app);
});
$bot->onCommand('start', fn($ctx) => telegram::start($ctx));
$bot->onCommand('contacts', fn($ctx) => telegram::contacts($ctx));
$bot->onCommand('company', fn($ctx) => telegram::company($ctx));
$bot->onCommand('community', fn($ctx) => telegram::community($ctx));
$bot->onCommand('settings', fn($ctx) => telegram::settings($ctx));
$bot->onText('💬 Контакты', fn($ctx) => telegram::contacts($ctx));
$bot->onText('🏛️ О компании', fn($ctx) => telegram::company($ctx));
$bot->onText('🎯 Сообщество', fn($ctx) => telegram::community($ctx));
$bot->onText('⚙️ Настройки', fn($ctx) => telegram::settings($ctx));
$bot->onCbQueryData(['mail'], fn($ctx) => telegram::_mail($ctx));
$bot->onCbQueryData(['import_request'], fn($ctx) => telegram::import_request($ctx));
$bot->onCbQueryData(['order'], fn($ctx) => telegram::order($ctx));
$bot->onCbQueryData(['tuning'], fn($ctx) => telegram::tuning($ctx));
$bot->onCbQueryData(['brands'], fn($ctx) => telegram::brands($ctx));
$bot->onCbQueryData(['receiver_sim_choose'], fn($ctx) => telegram::receiver_sim_choose($ctx));
$bot->onCbQueryData(['receiver_sim_request'], fn($ctx) => telegram::receiver_sim_request($ctx));
$bot->onCbQueryData(['receiver_sim_input'], fn($ctx) => telegram::receiver_sim_input($ctx));
$bot->onCbQueryData(['receiver_sim_write'], fn($ctx) => telegram::receiver_sim_write($ctx));
$bot->onCbQueryData(['receiver_name_choose'], fn($ctx) => telegram::receiver_name_choose($ctx));
$bot->onCbQueryData(['receiver_name_request'], fn($ctx) => telegram::receiver_name_request($ctx));
$bot->onCbQueryData(['receiver_name_input'], fn($ctx) => telegram::receiver_name_input($ctx));
$bot->onCbQueryData(['receiver_name_write'], fn($ctx) => telegram::receiver_name_write($ctx));
$bot->onException(function (Context $ctx, $exception) {
var_dump($exception);
});
// Инициализация middleware с обработкой аккаунта
$bot->middleware([telegram::class, "account"]);
// Инициализация middleware с обработкой технических работ разных уровней
$bot->middleware([telegram::class, "suspension"]);
// Запуск чат-робота
$bot->run();

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace mirzaev\huesos;
namespace mirzaev\arming_bot;
// Files of the project
use mirzaev\huesos\controllers\core as controller,
mirzaev\huesos\models\core as model,
mirzaev\huesos\models\socket;
use mirzaev\arming_bot\controllers\core as controller,
mirzaev\arming_bot\models\core as model,
mirzaev\arming_bot\models\socket;
// Framework for PHP
use mirzaev\minimal\core;

View File

@ -0,0 +1,11 @@
@charset "UTF-8";
section#account {
z-index: 999;
position: fixed;
bottom: 20px;
left: 20px;
height: 16px;
display: flex;
}

View File

@ -0,0 +1,20 @@
@charset "UTF-8";
@keyframes slide-down-revert {
0% {
transform: translate(0, 0%);
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
100% {
transform: translate(0, -100%);
clip-path: polygon(0% 100%, 100% 100%, 100% 200%, 0% 200%);
}
}
.slide.down.revert.animated {
animation-duration: var(--animation-duration, 0.2s);
animation-name: slide-down-revert;
animation-fill-mode: forwards;
animation-timing-function: cubic-bezier(1, 0, 1, 1);
}

View File

@ -7,10 +7,6 @@ main>section:is(#summary, #products, #delivery, #delivery_request) {
overflow: hidden;
}
main:not(:has(h2#title + section#delivery))>h2#title {
margin-bottom: 1rem;
}
main>section:is(#summary, #delivery) {
background-color: var(--tg-theme-section-bg-color);
}
@ -249,13 +245,12 @@ main>section#products>article.product>a {
}
main>section#products>article.product>a>img:first-of-type {
width: 7rem;
/* min-width: 5rem; */
/* width: 5rem; */
min-width: 5rem;
min-height: 100%;
object-fit: cover;
image-rendering: auto;
border-radius: 0.75rem;
border: 0.2rem solid var(--tg-theme-hint-color);
box-shadow: -5px 0px 50px 10px rgba(0, 0, 0, 0.4);
-webkit-box-shadow: -5px 0px 50px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: -5px 0px 50px 10px rgba(0, 0, 0, 0.4);
@ -281,17 +276,12 @@ main>section#products>article.product>div>div.head {
gap: 1rem;
}
main>section#products>article.product>div>div.head>button {
align-self: start;
}
main>section#products>article.product>div>div>button:first-of-type {
margin-left: auto;
}
main>section#products>article.product>div>div>button {
padding: 0.4rem;
color: var(--tg-theme-text-color);
background: unset;
}

View File

@ -0,0 +1,188 @@
@charset "UTF-8";
main>section#categories {
width: var(--width);
display: flex;
flex-flow: row wrap;
gap: var(--gap, 5px);
}
main>section#categories>a.category[type="button"] {
--padding: 0.7rem;
position: relative;
height: 23px;
padding: var(--padding);
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
overflow: hidden;
border-radius: 0.75rem;
color: var(--tg-theme-button-text-color);
background-color: var(--tg-theme-button-color);
}
main>section#categories:last-child {
/* margin-bottom: unset; */
}
main>section#categories>a.category[type="button"]:has(>img) {
min-width: calc(50% - var(--padding) * 2);
height: 180px;
padding: unset;
}
main>section#categories>a.category[type="button"]>img {
position: absolute;
left: -5%;
top: -5%;
width: 110%;
height: 110%;
object-fit: cover;
/* filter: blur(1px); */
filter: brightness(60%);
}
main>section#categories>a.category[type="button"]:is(:hover, :focus)>img {
filter: unset;
}
main>section#categories>a.category[type="button"]:has(>img)>p {
position: absolute;
left: var(--padding);
bottom: var(--padding);
right: var(--padding);
margin: unset;
width: min-content;
border-radius: 0.75rem;
background: var(--tg-theme-section-bg-color);
box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.3);
-moz-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.3);
}
main>section#categories>a.category[type="button"]>p {
z-index: 100;
padding: 8px 16px;
}
main>section#filters {
width: var(--width);
max-height: 2.5rem;
display: flex;
align-items: start;
}
main>section#products {
--column: calc((100% - var(--gap)) / 2);
width: var(--width);
display: grid;
grid-gap: var(--gap);
grid-template-columns: repeat(2, var(--column));
grid-auto-flow: row dense;
}
main>section#products>div.column {
width: 100%;
display: flex;
flex-direction: column;
gap: var(--gap);
}
main>section#products>div.column>article.product {
position: relative;
width: 100%;
display: flex;
flex-grow: 0;
flex-direction: column;
border-radius: 0.75rem;
overflow: clip;
cursor: pointer;
backdrop-filter: brightness(0.7);
background-color: var(--tg-theme-section-bg-color);
}
main>section#products>div.column>article.product:is(:hover, :focus) {
/* flex-grow: 0.1; */
/* background-color: var(--tg-theme-section-bg-color); */
}
main>section#products>div.column>article.product:is(:hover, :focus)>* {
transition: 0s;
}
main>section#products>div.column>article.product:not(:is(:hover, :focus))>* {
transition: 0.2s ease-out;
}
main>section#products>div.column>article.product>a>img:first-of-type {
width: 100%;
image-rendering: auto;
border-radius: 0.75rem;
box-shadow: 0px 5px 70px 20px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 5px 70px 20px rgba(0, 0, 0, 0.3);
-moz-box-shadow: 0px 5px 70px 20px rgba(0, 0, 0, 0.3);
}
main>section#products>div.column>article.product>a>img:first-of-type+* {
margin-top: auto;
}
main>section#products>div.column>article.product>a>p.title {
z-index: 50;
margin: unset;
padding: 4px 8px 8px;
font-size: 0.9rem;
font-weight: bold;
overflow-wrap: anywhere;
hyphens: auto;
}
main>section#products>div.column>article.product>a>p.title>span {
color: var(--tg-theme-hint-color);
}
main>section#products>div.column>article.product>div[data-product="buttons"]:last-of-type {
z-index: 100;
height: 33px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
border-radius: 0.75rem;
}
main>section#products>div.column>article.product[data-product-amount]:not(:is([data-product-amount="0"], [data-product-amount="1"]))>div[data-product="buttons"]:last-of-type {
container-type: inline-size;
container-name: product-buttons;
}
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"] {
padding: 0;
flex-grow: 1;
}
main>section#products>div.column>article.product:is([data-product-amount="0"], [data-product-amount="1"])>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-parameter="amount"],
main>section#products>div.column>article.product[data-product-amount="0"]>div[data-product="buttons"]>button:is([data-product-button="write"], [data-product-button="delete"]) {
display: none;
}
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-parameter="amount"]:after {
content: '*';
margin: 0 0.2rem;
}
main>section#products>div.column>article.product[data-product-amount]:not([data-product-amount="0"])>div[data-product="buttons"] {
filter: hue-rotate(calc(120deg + var(--hue-rotate-offset, 0deg)));
}
@container product-buttons (max-width: 200px) {
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"]>span:is([data-product-parameter="cost"], [data-product-parameter="currency"]) {
display: none;
}
main>section#products>div.column>article.product>div[data-product="buttons"]>button[data-product-button="toggle"]>span[data-product-parameter="amount"]:after {
content: unset;
}
}

View File

@ -26,7 +26,7 @@ i.icon.arrow:not(.circle, .square)::after {
bottom: 7px;
}
i.icon.arrow:not(.circle, .square, .short)::before {
i.icon.arrow:not(.circle, .square)::before {
width: 16px;
height: 2px;
bottom: 10px;

View File

@ -0,0 +1,126 @@
@charset "UTF-8";
section[data-type="select"] {
--width: max(14rem, 20vw);
--height-element: 2rem;
--height-close: var(--height-element);
--height-open: max-content;
position: relative;
width: var(--width);
height: var(--height-close);
display: flex;
flex-direction: column;
cursor: pointer;
border-radius: 0.75rem;
overflow-x: hidden;
background-color: var(--tg-theme-button-color);
transition: 0s;
}
section[data-type="select"]>button:has(>i.icon.close) {
align-self: end;
height: 100%;
padding: 0 0.4rem;
}
section[data-type="select"]:has(input[id$="title"]:checked)>button:has(>i.icon.close),
section[data-type="select"]:focus>button:has(>i.icon.close) {
display: none;
}
section[data-type="select"]:not(:focus, :has(>button>i.icon.close)):after {
z-index: 30;
content: '';
top: calc(50% - 2.5px);
right: 1rem;
position: absolute;
width: 0;
height: 0;
pointer-events: none;
border-left: 5px solid transparent;
border-top: 5px solid var(--tg-theme-button-text-color, black);
border-right: 5px solid transparent;
}
section[data-type="select"]>input {
left: -99999px;
position: absolute;
opacity: 0;
}
section[data-type="select"]>label {
z-index: 10;
order: 2;
top: 0;
position: absolute;
width: 100%;
height: var(--height-element);
padding: 0 1rem;
display: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
box-sizing: border-box;
pointer-events: none;
color: var(--tg-theme-button-text-color);
transition: 0s;
}
section[data-type="select"]:is([data-select="open"], :focus)>input:not(:checked)+label[for$='title'] {
display: none;
}
section[data-type="select"]:is([data-select="open"], :focus)>input:not(:checked)+label[for$='all']:not(:hover, :active, :focus) {
filter: brightness(90%);
}
section[data-type="select"]>input:not(:checked)+label {
cursor: pointer;
filter: brightness(80%);
}
section[data-type="select"]:is([data-select="open"], :focus)>input+label:hover {
filter: brightness(110%);
}
section[data-type="select"]:is([data-select="open"], :focus)>input:checked+label:hover {
filter: brightness(120%);
}
section[data-type="select"]:is([data-select="open"], :focus)>input+label:is(:active, :focus) {
filter: brightness(60%);
}
section[data-type="select"]:is([data-select="open"], :focus)>input:checked+label:is(:active, :focus) {
filter: brightness(70%);
}
section[data-type="select"]>input:checked+label {
order: 1;
max-width: calc(var(--width) - 2rem - 10px);
display: inline;
line-height: var(--height-element);
padding-right: 0;
}
section[data-type="select"]:is([data-select="open"], :focus)>input:checked+label {
max-width: initial;
padding-right: initial;
}
section[data-type="select"]:is([data-select="open"], :focus) {
height: var(--height-open, max-content);
}
section[data-type="select"]:is([data-select="open"], :focus)>label {
position: relative;
display: inline;
line-height: var(--height-element);
pointer-events: all;
}
@media only screen and (max-width: 500px) {
section[data-type="select"]:only-child {
--width: 100%
}
}

View File

@ -28,8 +28,7 @@ a {
body {
--gap: 16px;
/* --width: calc(100% - var(--gap) * 2); */
--width: calc(100vw - var(--gap) * 2 - 1rem);
--width: calc(100% - var(--gap) * 2);
--offset-x: 2%;
width: 100%;
height: 100%;
@ -50,10 +49,9 @@ body:has(section#window) {
aside {}
header {
z-index: 1000;
container-type: inline-size;
container-name: header;
position: relative;
margin-top: 2rem;
padding: 0 var(--offset-x);
display: flex;
flex-direction: column;
@ -61,12 +59,7 @@ header {
gap: 26px;
}
body:has(>div#top>search)>header {
margin-top: var(--header-margin-top, 2rem);
}
main {
--gap-main: calc(var(--gap) + 10px);
container-type: inline-size;
container-name: main;
flex-grow: 1;
@ -74,7 +67,7 @@ main {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--gap-main);
gap: calc(var(--gap) + 10px);
transition: 0s;
}
@ -108,7 +101,7 @@ main>article>h3 {
margin-top: 2rem;
}
main>search.relative {
main>search {
--gap: 16px;
--border-width: 1px;
width: var(--width);
@ -120,71 +113,7 @@ main>search.relative {
overflow: clip;
}
div#top {
z-index: 1000;
position: fixed !important;
top: 0;
padding: 0.6rem 1rem;
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
background-color: var(--tg-theme-bg-color);
transform: translate3d(0, 0, 0);
}
div#top>search.fixed {
--gap: 16px;
--border-width: 1px;
width: var(--width);
display: flex;
flex-flow: row;
border-radius: 1.375rem;
backdrop-filter: contrast(0.8);
overflow: hidden;
border: 2px solid transparent;
}
a#cart {
--size: 2.2rem;
position: fixed !important;
bottom: 1rem;
right: 1rem;
width: var(--size);
height: var(--size);
padding: 0.25rem 0.3rem 0.35rem 0.3rem;
border-radius: 0.75rem;
overflow: hidden;
background-color: var(--tg-theme-section-header-text-color);
box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
transform: translate3d(0, 0, 0);
}
a#cart:before {
content: var(--cart-amount);
position: absolute;
right: -0.5rem;
top: -0.6rem;
padding: 0.2rem 0.3rem;
min-width: 0.7rem;
display: flex;
justify-content: center;
align-items: center;
font-size: small;
border-radius: 1.125rem;
border: 0.2rem solid var(--tg-theme-button-color);
color: var(--tg-theme-button-color);
background-color: var(--tg-theme-button-text-color);
box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.2);
}
footer {
margin-bottom: var(--footer-margin-bottom);
}
footer {}
footer>section#govno {
align-items: center;
@ -327,7 +256,6 @@ input {
.cost.currency:after {
content: var(--currency);
margin-left: var(--currency-offset, 0.1rem);
font-weight: normal;
}
.cost.plus:before {

View File

@ -0,0 +1,54 @@
@charset "UTF-8";
header>nav#menu {
container-type: inline-size;
container-name: menu;
margin-bottom: 1rem;
width: var(--width);
min-height: 3rem;
display: flex;
flex-flow: row wrap;
gap: 1rem;
border-radius: 1.375rem;
overflow: hidden;
}
header>nav#menu>a[type="button"] {
height: 3rem;
padding: unset;
border-radius: 1.375rem;
color: var(--tg-theme-button-text-color);
background-color: var(--tg-theme-button-color);
}
header>nav#menu>a[type=button]>:first-child {
margin-left: 1rem;
}
header>nav#menu>a[type="button"]>* {
margin-right: 1rem;
}
@container header (max-width: 450px) {
header>nav#menu>a[type="button"]:nth-child(1)>i.icon+span {
display: none;
}
}
@container header (max-width: 350px) {
header>nav#menu>a[type="button"]:nth-child(2)>i.icon+span {
display: none;
}
}
@container header (max-width: 250px) {
header>nav#menu>a[type="button"]:nth-child(3)>i.icon+span {
display: none;
}
}
@container header (max-width: 150px) {
header>nav#menu>a[type="button"]>i.icon+span {
display: none;
}
}

View File

@ -78,7 +78,7 @@ section#window>div.card>div.images {
transition: 0s;
}
section#window>div.card>div.images:not(.extend):has(> img:last-child:nth-child(2)) {
section#window>div.card>div.images:has(> img:last-child:nth-child(2)) {
padding: 0rem 1rem;
}

Some files were not shown because too many files have changed in this diff Show More