Initial commit

This commit is contained in:
garden
2026-01-07 13:05:12 +05:00
commit 680a9659e3
151 changed files with 2912 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
!.gitignore
composer.phar
vendor

11
LICENSE Normal file
View File

@@ -0,0 +1,11 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

2
README.md Normal file
View File

@@ -0,0 +1,2 @@
# campanula
Receive and send messages through Gammu server and Telegram messenger interface

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace garden\campanula\controllers;
// Files of the project
use garden\campanula\views\templater,
garden\campanula\models\core as models;
// Library for languages support
use mirzaev\languages\language;
// Framework for PHP
use mirzaev\minimal\core as minimal,
mirzaev\minimal\controller,
mirzaev\minimal\http\response,
mirzaev\minimal\http\enumerations\status;
/**
* Controllers core
*
* @package garden\campanula\controllers
*
* @param language $language Language
* @param response $response Response
* @param array $errors Registry of errors
*
* @method void __construct(minimal $minimal) Constructor
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
class core extends controller
{
/**
* Language
*
* @var language $language Language
*/
protected language $language = language::en;
/**
* Response
*
* @see https://wiki.php.net/rfc/property-hooks (find a table about backed and virtual hooks)
*
* @var response $response Response
*/
protected response $response {
// Read
get => $this->response ??= $this->request->response();
}
/**
* Errors
*
* @var array $errors Registry of errors
*/
protected array $errors = [
'system' => []
];
/**
* Constructor
*
* @param minimal $core Instance of the MINIMAL
* @param bool $initialize Initialize a controller?
*
* @return void
*/
public function __construct(minimal $core)
{
// Blocking requests from CloudFlare (better to write this blocking into nginx config file)
if (isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return status::bruh->label;
// Initializing the view template engine instance
$this->view = new templater();
// For the extends system
parent::__construct(core: $core);
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace garden\campanula\controllers;
// Files of the project
use garden\campanula\controllers\core;
// Framework for PHP
use mirzaev\minimal\http\enumerations\content,
mirzaev\minimal\http\enumerations\status;
/**
* Index
*
* @package garden\campanula\controllers
*
* @param array $errors Registry of errors
*
* @method null index() Main page
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class index extends core
{
/**
* Errors
*
* @var array $errors Registry of errors
*/
protected array $errors = [
'system' => []
];
/**
* Main page
*
* @return null
*/
public function index(): null
{
if (str_contains($this->request->headers['accept'] ?? '', content::html->value)) {
// Request for HTML response
// Render page
$page = $this->view->render('index.html');
// Sending response
$this->response
->start()
->clean()
->sse()
->write($page)
->validate($this->request)
?->body()
->end();
// Deinitializing rendered page
unset($page);
// Exit (success)
return null;
}
// Exit (fail)
return null;
}
}

View File

@@ -0,0 +1,3 @@
!.gitignore
!*.php
*.baza

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace garden\campanula;
// Files of the project
use garden\campanula\models\account,
garden\campanula\models\authorizations,
garden\campanula\models\chat,
garden\campanula\models\tariff;
// Svoboda time
use svoboda\time\statement as svoboda;
// Baza database
use mirzaev\baza\record;
// Enabling debugging
/* ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1); */
// Initializing path to the public directory
define('INDEX', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'public');
// Initializing path to the root directory
define('ROOT', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
// Initializing path to the settings directory
define('SETTINGS', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'settings');
// Initializing path to the storage directory
define('STORAGE', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'storage');
// Initializing path to the databases directory
define('DATABASES', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'databases');
// Initializing path to the localizations directory
define('LOCALIZATIONS', INDEX . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'localizations');
// Initiailizing telegram data
require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php');
// Initializing dependencies
require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
// Initializing the account model
$account_model = new account();
// Searching for the account
$account = $account_model->database->read(
filter: fn(record $record) => $record->domain === 'buddy_volkodav',
amount: 1,
offset: 0
)[0] ?? null;
var_dump($account);
// Initializing the account authorizations model
$authorizations_model = new authorizations();
// Searching for the account authorizations
$authorizations = $authorizations_model->database->read(
filter: fn(record $record) => $record->account === $account->identifier,
amount: 1,
offset: 0
)[0] ?? null;
var_dump($authorizations);

View File

@@ -0,0 +1,42 @@
<?php
// Exit (success)
return [
// System
'empty' => 'Empty',
'yes' => 'Yes',
'no' => 'No',
// Main menu
'menu_title' => 'Main menu',
// Account
'account_title' => 'Account',
'account_authorized_system' => 'Access to the system',
'account_authorized_settings' => 'Access to settings',
'account_authorized_system_settings' => 'System access to the system settings',
// Language setting
'settings_select_language_title' => 'Select language',
'settings_select_language_description' => 'The selected language will be writed in your account settings',
'settings_language_update_success' => 'Language replaced:',
'settings_language_update_fail' => 'Failed to replace language',
// Language selection
'select_language_title' => 'Select language',
'select_language_description' => 'The selected language will be used in the current process',
'select_language_button_add' => 'Add a language',
// Authorization
'not_authorized_system' => 'You do not have access to the system',
'not_authorized_settings' => 'You do not have access to the settings',
'not_authorized_system_settings' => 'You do not have access to the system settings',
// Messages
'message_initialization_fail' => 'Не удалось инициализировать сообщение Телеграм',
'message_text_initialization_fail' => 'Не удалось инициализировать текст сообщения Телеграм',
// Other
'why_so_shroomious' => 'why so shroomious'
];

View File

@@ -0,0 +1,41 @@
<?php
// Выход (успех)
return [
// Система
'empty' => 'Пусто',
'yes' => 'Да',
'no' => 'Нет',
// Главное меню
'menu_title' => 'Главное меню',
// Аккаунт
'account_title' => 'Аккаунт',
'account_authorized_system' => 'Доступ к системе',
'account_authorized_settings' => 'Доступ к изменению настроек',
'account_authorized_system_settings' => 'Системный доступ к системным настройкам',
// Настройки языка
'settings_select_language_title' => 'Выбери язык',
'settings_select_language_description' => 'Выбранный язык будет записан в настройки аккаунта',
'settings_language_update_success' => 'Язык заменён:',
'settings_language_update_fail' => 'Не удалось заменить язык',
// Выбор языка
'select_language_title' => 'Выбери язык',
'select_language_description' => 'Выбранный язык будет использован в текущем процессе',
'select_language_button_add' => 'Добавить язык',
// Авторизация
'not_authorized_system' => 'У тебя нет доступа к системе',
'not_authorized_settings' => 'У тебя нет доступа к настройкам',
'not_authorized_system_settings' => 'У тебя нет доступа к системным настройкам',
// Сообщения
'message_initialization_fail' => 'Не удалось инициализировать сообщение Телеграм',
'message_text_initialization_fail' => 'Не удалось инициализировать текст сообщения Телеграм',
// Прочее
'why_so_shroomious' => 'почему такой грибъёзный'
];

View File

@@ -0,0 +1,377 @@
<?php
declare(strict_types=1);
namespace garden\campanula\models;
// Files of the project
use garden\campanula\models\core,
garden\campanula\models\authorizations,
garden\campanula\models\chat,
garden\campanula\models\settings,
garden\campanula\models\tariff,
garden\campanula\models\code,
garden\campanula\models\enumerations\tariff as tariff_type;
// The library for languages support
use mirzaev\languages\language;
// Baza database
use mirzaev\baza\database,
mirzaev\baza\column,
mirzaev\baza\record,
mirzaev\baza\enumerations\encoding,
mirzaev\baza\enumerations\type;
// Active Record pattern
use mirzaev\record\interfaces\record as record_interface,
mirzaev\record\traits\record as record_trait;
// Svoboda time
use svoboda\time\statement as svoboda;
// Framework for Telegram
use Zanzara\Telegram\Type\User as telegram_user;
// Built-in libraries
use Exception as exception,
RuntimeException as exception_runtime;
/**
* Account
*
* @package garden\campanula\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 account extends core implements record_interface
{
use record_trait;
/**
* File
*
* @var string $database Path to the database file
*/
protected string $file = DATABASES . DIRECTORY_SEPARATOR . 'accounts.baza';
/**
* Database
*
* @var database $database The database
*/
public protected(set) database $database;
/**
* Constructor
*
* @method record|null $record The record
*
* @return void
*/
public function __construct(?record $record = null)
{
// Initializing the database
$this->database = new database()
->encoding(encoding::utf8)
->columns(
new column('identifier', type::long_long_unsigned),
new column('identifier_telegram', type::long_long_unsigned),
new column('domain', type::string, ['length' => 32]),
new column('name_first', type::string, ['length' => 64]),
new column('name_second', type::string, ['length' => 64]),
new column('language', type::string, ['length' => 2]),
new column('robot', type::char),
/* new column('', type::), */
new column('updated', type::integer_unsigned),
new column('created', type::integer_unsigned)
)
->connect($this->file);
// Initializing the record
$record instanceof record and $this->record = $record;
}
/**
* Initialize
*
* @param telegram_user $telegram The telegram account
*
* @throws exception_runtime if update the account record in the database by the telegram account values
* @throws exception_runtime if failed to find the registered account
* @throws exception_runtime if failed to registrate the account
*
* @return static|null The account, if found, updated or created
*/
public function initialize(telegram_user $telegram): ?static
{
// Searching for the account in the database
$account = $this->database->read(filter: fn(record $record) => $record->identifier_telegram === $telegram->getId(), amount: 1)[0] ?? null;
if ($account instanceof record) {
// Found the account record
if (
$account->domain !== (string) $telegram->getUsername() ||
$account->name_first !== (string) $telegram->getFirstName() ||
$account->name_second !== (string) $telegram->getLastName() ||
$account->language !== (string) $telegram->getLanguageCode()
) {
// The telegram account was updated
// Updating the account in the database
$updated = $this->database->read(
filter: fn(record $record) => $record->identifier_telegram === $telegram->getId(),
update: function (record &$record) use ($telegram) {
// Writing new values into the record
$record->domain = (string) $telegram->getUsername();
$record->name_first = (string) $telegram->getFirstName();
$record->name_second = (string) $telegram->getLastName();
$record->language = (string) $telegram->getLanguageCode();
$record->updated = svoboda::timestamp();
},
amount: 1
)[0] ?? null;
if ($updated instanceof record && $updated->values() !== $account->values()) {
// Updated the account in the database
// Writing the updated record into the account object
$this->record = $updated;
// Deserializing parameters
$this->deserialize();
// Exit (success)
return $this;
} else {
// Not updated the account in the database
// Exit (fail)
throw new exception_runtime('Failed to update the account record in the database by the telegram account values');
}
}
// Writing the found record into the account object
$this->record = $account;
// Deserializing parameters
$this->deserialize();
// Exit (success)
return $this;
} else {
// Not found the account record
if ($this->registrate($telegram)) {
// Registered the account
// Searching for the registered account in the database
$registered = $this->database->read(filter: fn(record $record) => $record->identifier_telegram === $telegram->getId(), amount: 1)[0] ?? null;
if ($registered instanceof record) {
// Found the registered account
// Writing the registered record into the account object
$this->record = $registered;
// Deserializing parameters
$this->deserialize();
// Exit (success)
return $this;
} else {
// Not found the registered account
// Exit (fail)
throw new exception_runtime('Failed to find the registered account');
}
} else {
// Not registered the account
// Exit (fail)
throw new exception_runtime('Failed to registrate the account');
}
}
}
/**
* Registrate
*
* Create the account by the telegram account data
*
* @param telegram_user $telegram The telegram account
*
* @return record|false The record, if created
*/
public function registrate(telegram_user $telegram): record|false
{
// Creating the record
$record = $this->write(
telegram_identifier: (int) $telegram->getId(),
name_first: (string) $telegram->getFirstName(),
name_last: (string) $telegram->getLastName(),
domain: (string) $telegram->getUsername(),
language: (string) $telegram->getLanguageCode(),
robot: (bool) $telegram->isBot()
);
if ($record instanceof record) {
// The record was writed into the database
// Initializing the authorizations model
$authorizations = new authorizations();
// Creating the authorizations record
$authorizations->write(account: $record->identifier);
// Initializing the settings model
$settings = new settings();
// Creating the account settings
$settings->write(account: $record->identifier);
// Initializing the chat model
$chat = new chat();
// Creating the account chat
$record->chat = $chat->write(account: $record->identifier, network: NETWORK_DEFAULT);
// Initializing the tariff model
$tariff = new tariff();
// Creating the tariff record
$record->tariff = $tariff->write(account: $record->identifier, invoice: 0, active: 1, type: TARIFF_DEFAULT ?? tariff_type::free);
// Writing the record into the database
$record = $this->database->read(
filter: fn(record $_record) => $_record->identifier === $record->identifier,
update: fn(record &$_record) => $_record = $record,
amount: 1
)[0] ?? null;
// Exit (success)
return $record;
}
// Exit (fail)
return false;
}
/**
* Write
*
* @param int $telegram_identifier The telegram account identifier
* @param string $name_first
* @param string $name_last
* @param string $domain
* @param language|string $language
* @param bool $robot Is a robot?
*
* @return record|false The record, if created
*/
public function write(
int $telegram_identifier,
string $domain = '',
string $name_first = '',
string $name_last = '',
language|string $language = LANGUAGE_DEFAULT ?? language::en,
bool $robot = false,
): record|false {
// Initializing the record
$record = $this->database->record(
$this->database->count() + 1,
(int) $telegram_identifier,
$domain,
$name_first,
$name_last,
$language instanceof language ? $language->name : (string) $language,
(int) $robot,
/* */
svoboda::timestamp(),
svoboda::timestamp()
);
// Writing the record into the database
$created = $this->database->write($record);
// Exit (success)
return $created ? $record : false;
}
/**
* Serialize
*
* @return self The instance from which the method was called (fluent interface)
*/
public function serialize(): self
{
// Serializing the record parameters
$this->record->language = $this->record->language->name;
// Exit (success)
return $this;
}
/**
* Deserialize
*
* @return self The instance from which the method was called (fluent interface)
*/
public function deserialize(): self
{
// Deserializing the record parameters
$this->record->language = language::{$this->record->language} ?? LANGUAGE_DEFAULT ?? language::en;
// Exit (success)
return $this;
}
/**
* Authorizations
*
* Search for the account authorizations
*
* @return authorizations|null The account authorizations
*/
public function authorizations(): ?authorizations
{
// Search for the account authorizations
$authorizations = new authorizations()->read(filter: fn(record $record) => $record->active === 1 && $record->account === $this->identifier);
if ($authorizations instanceof authorizations) {
// Found the account authorizations
// Exit (success)
return $authorizations;
}
// Exit (fail)
return null;
}
/**
* Settings
*
* Search for the account settings
*
* @return settings|null The account settings
*/
public function settings(): ?settings
{
// Search for the account settings
$settings = new settings()->read(filter: fn(record $record) => $record->active === 1 && $record->account === $this->identifier);
if ($settings instanceof settings) {
// Found the account settings
// Exit (success)
return $settings;
}
// Exit (fail)
return null;
}
}

View File

@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace garden\campanula\models;
// Files of the project
use garden\campanula\models\core;
// Baza database
use mirzaev\baza\database,
mirzaev\baza\column,
mirzaev\baza\record,
mirzaev\baza\enumerations\encoding,
mirzaev\baza\enumerations\type;
// Active Record pattern
use mirzaev\record\interfaces\record as record_interface,
mirzaev\record\traits\record as record_trait;
// Svoboda time
use svoboda\time\statement as svoboda;
// Framework for Telegram
use Zanzara\Telegram\Type\User as telegram;
// Built-in libraries
use Exception as exception,
RuntimeException as exception_runtime;
/**
* Authorizations
*
* @package garden\campanula\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 authorizations extends core implements record_interface
{
use record_trait;
/**
* File
*
* @var string $database Path to the database file
*/
protected string $file = DATABASES . DIRECTORY_SEPARATOR . 'authorizations.baza';
/**
* Database
*
* @var database $database The database
*/
public protected(set) database $database;
/**
* Constructor
*
* @method record|null $record The record
*
* @return void
*/
public function __construct(?record $record = null)
{
// Initializing the database
$this->database = new database()
->encoding(encoding::utf8)
->columns(
new column('identifier', type::long_long_unsigned),
new column('account', type::long_long_unsigned),
new column('system', type::char),
new column('settings', type::char),
new column('chat', type::char),
/* new column('', type::char), */
new column('system_settings', type::char),
new column('updated', type::integer_unsigned),
new column('created', type::integer_unsigned)
)
->connect($this->file);
// Initializing the record
$record instanceof record and $this->record = $record;
}
/**
* Write
*
* @param int $account The account identifier
* @param int $system
* @param int $settings
* @param int $system_settings
*
* @return int|false The record identifier, if created
*/
public function write(
int $account,
int $system = 1,
int $settings = 1,
int $system_settings = 0,
): int|false
{
$record = $this->database->record(
$this->database->count() + 1,
$account,
$system,
$settings,
$system_settings,
svoboda::timestamp(),
svoboda::timestamp()
);
// Writing the record into the database
$created = $this->database->write($record);
// Exit (success)
return $created ? $record->identifier : false;
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace garden\campanula\models;
// Framework for PHP
use mirzaev\minimal\model,
mirzaev\minimal\http\enumerations\status;
// Built-in libraries
use exception;
/**
* Models core
*
* @package garden\campanula\models
*
* @method void __construct() Constructor
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
class core extends model
{
/**
* File
*
* @var string database Path to the database file
*/
protected string $file = DATABASES . DIRECTORY_SEPARATOR . 'example.baza';
/**
* Constructor
*
* Initialize the database
*
* @return void
*/
public function __construct()
{
}
}

View File

@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace garden\campanula\models;
// Files of the project
use garden\campanula\models\core,
garden\campanula\models\tariff,
garden\campanula\models\enumerations\tariff as tariff_type,
garden\campanula\models\enumerations\acquiring as acquiring;
// Baza database
use mirzaev\baza\database,
mirzaev\baza\column,
mirzaev\baza\record,
mirzaev\baza\enumerations\encoding,
mirzaev\baza\enumerations\type;
// Active Record pattern
use mirzaev\record\interfaces\record as record_interface,
mirzaev\record\traits\record as record_trait;
// Svoboda time
use svoboda\time\statement as svoboda;
// Built-in libraries
use Exception as exception,
RuntimeException as exception_runtime;
/**
* Settings
*
* @package garden\campanula\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 settings extends core implements record_interface
{
use record_trait;
/**
* File
*
* @var string $database Path to the database file
*/
protected string $file = DATABASES . DIRECTORY_SEPARATOR . 'settings.baza';
/**
* Database
*
* @var database $database The database
*/
public protected(set) database $database;
/**
* Constructor
*
* @method record|null $record The record
*
* @return void
*/
public function __construct(?record $record = null)
{
// Initializing the database
$this->database = new database()
->encoding(encoding::utf8)
->columns(
new column('identifier', type::long_long_unsigned),
new column('account', type::long_long_unsigned),
/* new column('', type::), */
new column('updated', type::integer_unsigned),
new column('created', type::integer_unsigned)
)
->connect($this->file);
// Initializing the record
$record instanceof record and $this->record = $record;
}
/**
* Write
*
* @param int $account The account identifier (0 for disable)
*
* @return int|false The record identifier, if created
*/
public function write(
int $account = 0,
): int|false {
$record = $this->database->record(
$this->database->count() + 1,
$account,
svoboda::timestamp(),
svoboda::timestamp()
);
// Writing the record into the database
$created = $this->database->write($record);
// Exit (success)
return $created ? $record->identifier : false;
}
}

View File

@@ -0,0 +1,393 @@
<?php
declare(strict_types=1);
namespace garden\campanula\models\telegram;
// Files of the project
use garden\campanula\models\core,
garden\campanula\models\account,
garden\campanula\models\settings,
garden\campanula\models\telegram\settings as telegram_settings,
garden\campanula\models\telegram\processes\language\select as process_language_select;
// Library for languages support
use mirzaev\languages\language;
// The library for escaping all markdown symbols
use function mirzaev\unmarkdown;
// Framework for Telegram
use Zanzara\Context as context,
Zanzara\Telegram\Type\Message as message,
Zanzara\Telegram\Type\Input\InputFile as file_input;
/**
* Telegram commands
*
* @package garden\campanula\models\telegram
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class commands extends core
{
/**
* Start
*
* Responce for command: "/start"
*
* @param context $context Request data from Telegram
*
* @return void
*/
public static function start(context $context): void
{
static::menu($context);
}
/**
* Menu
*
* Responce for command: '/menu'
*
* @param context $context Request data from Telegram
*
* @return void
*/
public static function menu(context $context): void
{
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing the language
$language = $context->get('language');
if ($language instanceof language) {
// Initialized the language
// Initializing localization
$localization = $context->get('localization');
if ($localization) {
// Initialized localization
// Initializing the title
$title = '📋 *' . $localization['menu_title'] . '*';
// Initializing accounts
/* $accounts_message = '*' . $localization['menu_accounts'] . ':* ' . ((new account)->database->count() ?? 0); */
// Initializing the account tariff
$tariff = $account->tariff();
// Declaring the tariff button
$tariff_button = [];
if ($tariff instanceof tariff) {
// Initialized the account tariff
// Initializing the tariff button
$tariff_button = [
'text' => '🔐 ' . $tariff->type->label($language) . ' (' . $tariff->used . '/' . $tariff->tokens . ')',
'callback_data' => 'tariffs'
];
} else {
// Initialized the account tariff
// Initializing the tariff button
$tariff_button = [
'text' => '⚠️ ' . $localization['menu_tariff_empty'],
'callback_data' => 'tariffs'
];
}
// Initializing the account chat
$chat = $account->chat();
// Initializing the chto text
$howto = $localization['menu_howto'];
// Sending the message
$context->sendMessage(
<<<TXT
$title
$howto
TXT,
[
'reply_markup' => [
'inline_keyboard' => [
/* [
[
'text' => '⚙️ ' . $localization[''],
'callback_data' => ''
]
] */
],
'disable_notification' => true,
'remove_keyboard' => true
],
]
)->then(function (message $message) use ($context) {
// Sended the message
});
} else {
// Not initialized localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized language
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize language*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* Account
*
* Responce for the command: "/account"
*
* Sends information about account with menu
*
* @param context $context Request data from Telegram
*
* @return void
*/
public static function account(context $context): void
{
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing localization
$localization = $context->get('localization');
if ($localization) {
// Initialized localization
// Initializing title for the message
$title = '🫵 ' . $localization['account_title'];
// Declaring buufer of rows about authorizations
$authorizations = '';
// Initializing rows about authorization
foreach ($account->values() as $key => $value) {
// Iterating over account parameters
if (str_starts_with($key, 'authorized_')) {
// Iterating over account authorizations
// Skipping system authorizations
if (str_starts_with($key, 'authorized_system_')) continue;
// Writing into buffer of rows about authorizations
$authorizations .= ($value ? '✅' : '❎') . ' *' . ($localization["account_$key"] ?? $key) . ':* ' . ($value ? $localization['yes'] : $localization['no']) . "\n";
}
}
// Trimming the last line break character
$authorizations = trim($authorizations, "\n");
// Sending the message
$context->sendMessage(
<<<TXT
$title
$authorizations
TXT,
[
'reply_markup' => [
'remove_keyboard' => true,
'disable_notification' => true
],
'link_preview_options' => [
'is_disabled' => true
]
]
);
} else {
// Not initialized localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* Language
*
* Responce for the command: "/language"
*
* Send the language selection menu
*
* @param context $context Request data from Telegram
*
* @return void
*/
public static function language(context $context): void
{
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing language
$language = $context->get('language');
if ($language instanceof language) {
// Initialized language
// Initializing localization
$localization = $context->get('localization');
if ($localization) {
// Initialized localization
// Sending the language selection
process_language_select::menu(
context: $context,
prefix: 'settings_language_',
title: '🌏 *' . $localization['settings_select_language_title'] . '*',
description: '🌏 *' . $localization['settings_select_language_description'] . '*'
);
} else {
// Not initialized localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized language
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize language*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* Society
*
* Responce for the command: "/society"
*
* Sends the "mushroom" image and the localized text "why so shroomious"
*
* @param context $context Request data from Telegram
*
* @return void
*/
public static function society(context $context): void
{
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing localization
$localization = $context->get('localization');
if ($localization) {
// Initialized localization
// Sending the message
$context->sendPhoto(
new file_input(STORAGE . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'mushroom.jpg'),
[
'caption' => $localization['why_so_shroomious'],
'disable_notification' => true
]
);
} else {
// Not initialized localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Ending the conversation process
$context->endConversation();
});
}
}
}

View File

@@ -0,0 +1,469 @@
<?php
declare(strict_types=1);
namespace garden\campanula\models\telegram;
// Files of the project
use garden\campanula\models\core,
garden\campanula\models\account,
garden\campanula\models\authorizations;
// The library for languages support
use mirzaev\languages\language;
// The library for escaping all markdown symbols
use function mirzaev\unmarkdown;
// Framework for Telegram
use Zanzara\Context as context,
Zanzara\Telegram\Type\Message as message,
Zanzara\Middleware\MiddlewareNode as node;
// Built-in libraries
use Error as error;
/**
* Telegram middlewares
*
* @package garden\campanula\models\telegram
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class middlewares extends core
{
/**
* Account (middleware)
*
* Initialize or registrate the account and write it into the `account` variable inside the `$context`
*
* @param context $context
* @param node $next
*
* @return void
*/
public static function account(context $context, node $next): void
{
// Is the process stopped?
if ($context->get('stop')) return;
// Initializing the telegram account
$telegram = $context->getEffectiveUser();
// Initializing the account
$account = new account()->initialize($telegram);
if ($account instanceof account) {
// Initialized the account
// Writing the account into the context variable
$context->set('account', $account);
// Continuation of the process
$next($context);
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* Authorizations (middleware)
*
* Initialize the account authorizations and write them into the `authorizations` variable inside the `$context`
*
* @param context $context
* @param node $next
*
* @return void
*/
public static function authorizations(context $context, node $next): void
{
// Is the process stopped?
if ($context->get('stop')) return;
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing the account authorizations
$authorizations = $account->authorizations();
if ($authorizations instanceof authorizations) {
// Initialized the account authorizations
// Writing the account authorizations into the context variable
$context->set('authorizations', $authorizations);
// Continuation of the process
$next($context);
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account authorizations*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* Language (middleware)
*
* Implement the account language
*
* @param context $context
* @param node $next
*
* @return void
*/
public static function language(context $context, node $next): void
{
// Is the process stopped?
if ($context->get('stop')) return;
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
if ($account->language instanceof language) {
// Initialized the language parameter
try {
// Writing the account language into the context variable
$context->set('language', $account->language);
} catch (error $error) {
// Not initialized the language
// Writing the default language into the context variable
$context->set('language', LANGUAGE_DEFAULT ?? language::en);
}
} else {
// Not initialized the language parameter
// Writing the default language into the context variable
$context->set('language', LANGUAGE_DEFAULT ?? language::en);
}
// Continuation of the process
$next($context);
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* Localization (middleware)
*
* Implement the account language and initialize the localization file
*
* @param context $context
* @param node $next
*
* @return void
*/
public static function localization(context $context, node $next): void
{
// Is the process stopped?
if ($context->get('stop')) return;
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing the language
$language = $context->get('language');
if ($language instanceof language) {
// Initialized the language
// Initializing path to the localization file
$file = LOCALIZATIONS . DIRECTORY_SEPARATOR . strtolower($language->label()) . '.php';
if (file_exists($file) && is_readable($file)) {
// Found the localization file
// Initializing localization
$localization = require($file);
if (is_array($localization)) {
// Initialized the localization
// Writing localization into the context variable
$context->set('localization', $localization);
// Continuation of the process
$next($context);
} else {
// Not initialized the localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not found the localization file
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize the localization file*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized language
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize language*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* Settings (middleware)
*
* Check the account for access to the settings
*
* @param context $context
* @param node $next
*
* @return void
*/
public static function settings(context $context, node $next): void
{
// Is the process stopped?
if ($context->get('stop')) return;
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing the account authorizations
$authorizations = $context->get('authorizations');
if ($authorizations instanceof authorizations) {
// Initialized the account authorizations
if ($authorizations->settings) {
// Authorized the account to the settings
// Continuation of the process
$next($context);
} else {
// Not authorized the account to the settings
// Initializing localization
$localization = $context->get('localization');
if ($localization) {
// Initialized localization
// Sending the message
$context->sendMessage('⛔ *' . $localization['not_authorized_settings'] . '*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
// Stopping the process
$context->set('stop', true);
} else {
// Not initialized localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
} else {
// Not initialized the account authorizations
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account authorizations*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
/**
* System settings (middleware)
*
* Check the account for access to the system settings
*
* @param context $context
* @param node $next
*
* @return void
*/
public static function system_settings(context $context, node $next): void
{
// Is the process stopped?
if ($context->get('stop')) return;
// Initializing the account
$account = $context->get('account');
if ($account instanceof account) {
// Initialized the account
// Initializing the account authorizations
$authorizations = $context->get('authorizations');
if ($authorizations instanceof authorizations) {
// Initialized the account authorizations
if ($authorizations->system_settings) {
// Authorized the account to the system settings
// Continuation of the process
$next($context);
} else {
// Not authorized the account to the system settings
// Initializing localization
$localization = $context->get('localization');
if ($localization) {
// Initialized localization
// Sending the message
$context->sendMessage('⛔ *' . $localization['not_authorized_system_settings'] . '*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
// Stopping the process
$context->set('stop', true);
} else {
// Not initialized localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account authorizations*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
}

View File

@@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
namespace garden\campanula\models\telegram\processes\language;
// Files of the project
use garden\campanula\models\core;
// Library for languages support
use mirzaev\languages\language;
// Baza database
use mirzaev\baza\record;
// Framework for Telegram
use Zanzara\Context as context,
Zanzara\Telegram\Type\Message as message;
/**
* Telegram language select
*
* @package garden\campanula\models\telegram\processes\language
*
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
final class select extends core
{
/**
* Language
*
* Send the language choose menu
*
* @param context $context Request data from Telegram
* @param string $prefix Prefix for 'callback_data' (`$prefix . $language->name`)
* @param string $title Title of the message
* @param string $description Description of the message
* @param array $exclude Languages that will be excluded ['ru', 'en'...]
*
* @return void
*/
public static function menu(context $context, string $prefix, string $title, string $description, array $exclude = []): void
{
// Initializing the account
$account = $context->get('account');
if ($account instanceof record) {
// Initialized the account
// Initializing language
$language = $context->get('language');
if ($language) {
// Initialized language
// Initializing localization
$localization = $context->get('localization');
if ($localization) {
// Initialized localization
// Declaring the buffer of generated keyboard with languages
$keyboard = [];
// Initializing the iterator of rows
$row = 0;
// Initializing buffer of languages
$languages = language::cases();
// Deleting the actual language from buffer of languages
unset($languages[array_search($language, $languages, strict: true)]);
// Sorting buffer of languages by the actual language
$languages = [$language, ...$languages];
foreach ($languages as $language) {
// Iterating over languages
// Skipping excluded languages
if (array_search($language->name, $exclude, strict: true) !== false) continue;
// Initializing the row
$keyboard[$row] ??= [];
// Writing the language choose button into the buffer of generated keyboard with languages
$keyboard[$row][] = [
'text' => ($language->flag() ? $language->flag() . ' ' : '') . $language->label($language),
'callback_data' => $prefix . $language->name
];
// When reaching 4 buttons in a row, move to the next row
if (count($keyboard[$row]) === 4) ++$row;
}
// Writing the button for helping lozalizing
$keyboard[$row === 0 && empty($keyboard[0]) ? 0 : ++$row] = [
[
'text' => '🗂 ' . $localization['select_language_button_add'],
'url' => 'https://git.svoboda.works/garden\campanula/src/branch/stable/kodorvan/neurobot/system/localizations'
]
];
// Sending the message
$context->sendMessage(
$title ?? '🌏 *' . $localization['select_language_title'] . "*\n" . ($description ?? $localization['select_language_description']),
[
'reply_markup' => [
'inline_keyboard' => $keyboard,
'disable_notification' => true
],
]
);
} else {
// Not initialized localization
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize localization*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized language
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize language*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
} else {
// Not initialized the account
// Sending the message
$context->sendMessage('⚠️ *Failed to initialize your Telegram account*')
->then(function (message $message) use ($context) {
// Sended the message
// Ending the conversation process
$context->endConversation();
});
}
}
}

View File

@@ -0,0 +1,34 @@
@font-face {
font-family: 'DejaVu';
src: url("/fonts/dejavu/DejaVuLGCSans-ExtraLight.ttf");
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: 'DejaVu';
src: url("/fonts/dejavu/DejaVuLGCSans.ttf");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'DejaVu';
src: url("/fonts/dejavu/DejaVuLGCSans-Oblique.ttf");
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'DejaVu';
src: url("/fonts/dejavu/DejaVuLGCSans-Bold.ttf");
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'DejaVu';
src: url("/fonts/dejavu/DejaVuLGCSans-BoldOblique.ttf");
font-weight: 500;
font-style: italic;
}

View File

@@ -0,0 +1,139 @@
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-Hair.woff2') format('woff2'), url('/fonts/fira/FiraSans-Hair.woff') format('woff');
font-weight: 100;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-HairItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-HairItalic.woff') format('woff');
font-weight: 100;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-UltraLight.woff2') format('woff2'), url('/fonts/fira/FiraSans-UltraLight.woff') format('woff');
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-UltraLightItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-UltraLightItalic.woff') format('woff');
font-weight: 200;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-Light.woff2') format('woff2'), url('/fonts/fira/FiraSans-Light.woff') format('woff');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-LightItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-LightItalic.woff') format('woff');
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-Regular.woff2') format('woff2'), url('/fonts/fira/FiraSans-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-Italic.woff2') format('woff2'), url('/fonts/fira/FiraSans-Italic.woff') format('woff');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraMono-Medium.woff2') format('woff2'), url('/fonts/fira/FiraMono-Medium.woff') format('woff');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-MediumItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-MediumItalic.woff') format('woff');
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-SemiBold.woff2') format('woff2'), url('/fonts/fira/FiraSans-SemiBold.woff') format('woff');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-SemiBoldItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-SemiBoldItalic.woff') format('woff');
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-Bold.woff2') format('woff2'), url('/fonts/fira/FiraSans-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-BoldItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-BoldItalic.woff') format('woff');
font-weight: 700;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-ExtraBold.woff2') format('woff2'), url('/fonts/fira/FiraSans-ExtraBold.woff') format('woff');
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-ExtraBoldItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-ExtraBoldItalic.woff') format('woff');
font-weight: 800;
font-style: italic;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-Heavy.woff2') format('woff2'), url('/fonts/fira/FiraSans-Heavy.woff') format('woff');
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: 'Fira';
src: url('/fonts/fira/FiraSans-HeavyItalic.woff2') format('woff2'), url('/fonts/fira/FiraSans-HeavyItalic.woff') format('woff');
font-weight: 900;
font-style: italic;
}
@font-face {
font-family: 'Fira' Mono;
src: url('/fonts/fira/FiraMono-Regular.woff2') format('woff2'), url('/fonts/fira/FiraMono-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Fira' Mono;
src: url('/fonts/fira/FiraMono-Bold.woff2') format('woff2'), url('/fonts/fira/FiraMono-Bold.woff') format('woff');
font-weight: 600;
font-style: normal;
}

View File

@@ -0,0 +1,31 @@
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-regular.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-regular.woff?sha=3114f1256') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-bold.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-bold.woff?sha=3114f1256') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-italic.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-italic.woff?sha=3114f1256') format('woff');
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: 'Hack';
src: url('/fonts/hack/hack-bolditalic.woff2?sha=3114f1256') format('woff2'), url('/fonts/hack/hack-bolditalic.woff?sha=3114f1256') format('woff');
font-weight: 700;
font-style: italic;
font-display: swap;
}

Binary file not shown.

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