From 1939387956fe1f63fa9900f4ee7dad56b56fe0fe Mon Sep 17 00:00:00 2001 From: mirzaev Date: Sat, 8 Mar 2025 20:02:17 +0700 Subject: [PATCH] downloads counter and week selection --- README.md | 42 +++ .../system/models/interneturok.php | 213 +++++++----- .../system/models/telegram/parser.php | 323 +++++++++++++++--- .../system/public/telegram.php | 28 ++ 4 files changed, 469 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index 323ec35..ed6b399 100755 --- a/README.md +++ b/README.md @@ -1,2 +1,44 @@ # parser_from_interneturok +Chat-robot Telegram for parsing homeworks by subject name, grade and number of the week +## 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) +5. [Zanzara](https://github.com/badfarm/zanzara) (Telegram framework + ReactPHP) +6. [Baza](https://git.svoboda.works/mirzaev/baza) (binary database) +7. [NGINX](https://nginx.org/en/) (web server) *(can be replaced)* +8. [SystemD](https://systemd.io/) (service manager) *(can be replaced)* + +You can find other dependencies in the file `/composer.json` + +## Installation + +### SystemD (or any alternative you like) +You can copy an example of systemd file from here: `/examples/systemd/ parser_from_interneturok-telegram.service`

+**Execute:** `sudo cp parser_from_interneturok-telegram.service /etc/systemd/system/ parser_from_interneturok-telegram.service && sudo chmod +x /etc/systemd/system/ parser_from_interneturok-telegram.service`

+*before you execute the command think about **what it does** and whether the **paths** are specified correctly*
+*the configuration file is very simple and you can remake it for any alternative to SystemD that you like* + +### InternetUrok accounts +Fill in the file: `/mirzaev/parser_from_interneturok/system/storage/accounts.csv`

+**Format:** "mail password" (**separated by 1 space symbol**) + +```CSV +mail password +mail password +mail password +``` + +### Authorized Telegram accounts +Fill in the file: `/mirzaev/parser_from_interneturok/system/settings/accounts.php` +```php +return [ + 1053489457, // Arsen Mirzaev Tatyano-Muradovich @redloser +] +``` +You can get the telegram account identifier by [@RawDataBot](https://t.me/RawDataBot) + +### Chat-robot Telegram token +Fill in the file: `/mirzaev/parser_from_interneturok/system/settings/telegram.php`
+You can get the chat-robot telegram token by [@BotFather](https://t.me/BotFather) diff --git a/mirzaev/parser_from_interneturok/system/models/interneturok.php b/mirzaev/parser_from_interneturok/system/models/interneturok.php index c214fab..e0ad351 100644 --- a/mirzaev/parser_from_interneturok/system/models/interneturok.php +++ b/mirzaev/parser_from_interneturok/system/models/interneturok.php @@ -163,11 +163,12 @@ final class interneturok extends core * * @param subject $subject The subject * @param int $grade The grade + * @param datetime $date The date * @param int $waiting Interval for processing requests (seconds) * * @return array|false Downloaded homework files */ - public function parse(subject $subject, int $grade, int $waiting = 3): array|false + public function parse(subject $subject, int $grade, datetime $date, int $week, int $waiting = 3): array|false { // Initializing accounts $accounts = static::accounts(); @@ -185,7 +186,6 @@ final class interneturok extends core sleep($waiting); try { - if ($this->authentication($account)->wait()) { // Authenticated the account @@ -196,7 +196,7 @@ final class interneturok extends core sleep($waiting); // Initializing the user journal - $journal = $this->journal(grade: $grade)->wait(); + $journal = $this->journal(grade: $grade, week: $week)->wait(); if (!empty($journal)) { // Initialized the user journal @@ -204,9 +204,6 @@ final class interneturok extends core // Writing into the output buffer echo "Инициализирован журнал\n"; - // Initializing the actual date - $date = new datetime(); - // Initializing the homeworks database $model = new homework(); @@ -229,16 +226,16 @@ final class interneturok extends core // Initialized the target subject event (the homework exists) if (new datetime($event->date) <= $date && new datetime($event->date)->modify('+6 days') >= $date) { - // Found the current event + // Found the target event week if ($event->subject?->name === $subject->value && $event->subject->grade === $grade) { // Found the target subject // Writing into the output buffer - /* echo "Найден школьный предмет: $subject->value для $grade класса (с " . new datetime($event->date)->format('d.m') . ' по ' . new datetime($event->date)->modify('+6 days')->format('d.m') . ")\n"; */ + echo "Найден школьный предмет: $subject->value для $grade класса (с " . new datetime($event->date)->format('d.m') . ' по ' . new datetime($event->date)->modify('+6 days')->format('d.m') . ")\n"; - // Initializing the unblock time - $unblock = svoboda::timestamp() - 31536000; + // Initializing the current time + $now = svoboda::timestamp(); foreach ($event->homeworks as $homework) { // Iterating over scheduled event homeworks @@ -246,60 +243,53 @@ final class interneturok extends core if ($homework->status === 'checked' && $homework->mark === 5) { // Homework checked and completed for a grade of 5 - // Checking for the block record - $blocked = $model->database->read( - filter: fn(record $record) => $record->identifier === $homework->item_id && $record->created > $unblock, - amount: 1 - )[0] ?? null; - if ($blocked instanceof record) { - // The account homework has been downloaded for 1 year + // Writing into the output buffer + echo "Найдено домашнее задание: $homework->item_id с оценкой $homework->mark\n"; - continue 3; - } else { - // The account homework has not been downloaded for 1 year + // Waiting for processing the request + sleep($waiting); + + // Initializing the homework files + $files = $this->homework(lesson: $event->lesson->id, homework: $homework->item_id)->wait(); + + if (!empty($files)) { + // Initialized the homework files // Writing into the output buffer - echo "Найдено домашнее задание: $homework->item_id с оценкой $homework->mark\n"; + echo "Получено домашнее задание\n"; - // Waiting for processing the request - sleep($waiting); + // Declaring the homework downloads for 1 last year counter + $year = 0; - // Initializing the homework files - $files = $this->homework(lesson: $event->lesson->id, homework: $homework->item_id)->wait(); + // Counting the homework downloading for 1 last year + $model->database->read( + filter: function (record $record) use ($homework, $now, &$year) { + if ($record->identifier === $homework->item_id && $now - $record->created < 31536000) { + // Found a downloading for 1 last year - if (!empty($files)) { - // Initialized the homework files - - // Writing into the output buffer - echo "Получено домашнее задание\n"; - - // Blocking the homework downloading for 1 year - $blocked = $model->create(identifier: $homework->item_id); - - if ($blocked !== false) { - // The homework was blocked for 1 year - - // Writing into the output buffer - echo "Заблокировано на 1 год домашнее задание\n"; + // Increasing the homework downloads for 1 last year counter + ++$year; + } // Exit (success) - return $files; - } - } + return false; + }, + amount: 100 + ); + + // Writing the homework downloading record + $model->create(identifier: $homework->item_id); + + // Exit (success) + return ['downloads' => ['year' => $year], 'files' => $files]; } } } } - - // Stopping processing events and starting processing the next account - break 2; } } } - - // Stopping processing events and starting processing the next account - break; } } } else { @@ -458,10 +448,11 @@ final class interneturok extends core * Search for the user homeworks journal * * @param int $grade The grade + * @param int $week Number of the week * * @return promise|null The journal data (object) */ - private function journal(int $grade): promise|null + private function journal(int $grade, int $week): promise|null { if (!empty($this->token)) { // Initialized the account authorization token @@ -476,7 +467,7 @@ final class interneturok extends core ); return $this->browser->sendAsync($request) - ->then(function ($response) use ($grade) { + ->then(function ($response) use ($grade, $week) { // Sended the request and received the response // Initializing the user data @@ -501,7 +492,7 @@ final class interneturok extends core ); return $this->browser->sendAsync($request) - ->then(function ($response) use ($grade, $identifier) { + ->then(function ($response) use ($grade, $identifier, $week) { // Sended the request and received the response // Initializing the user schedules @@ -529,7 +520,7 @@ final class interneturok extends core ); return $this->browser->sendAsync($request) - ->then(function ($response) use ($grade, $identifier, $year) { + ->then(function ($response) use ($grade, $identifier, $year, $week) { // Sended the request and received the response // Initializing the moscow time @@ -542,51 +533,42 @@ final class interneturok extends core $now = new datetime($time); // Declaring the quarter buffer - $quarter = null; + $quarter = static::quarter(number: $week); - foreach ($year->quarters as $value) { - // Iterating over study year quarters + if ($quarter !== false) { + // Initialized number of the quarter - // Implementing the quarter start time and end time - $start = new datetime($value->starts_at); - $end = new datetime($value->ends_at); + // Initializing the request to the user journal API + $request = new request( + 'GET', + static::JOURNAL . "?grade=$grade&quarter=$quarter&year_id=$year->id&user_id=$identifier&token=$this->token" + ); - if ($now > $start && $now < $end) { - // Found the current quarter + return $this->browser->sendAsync($request) + ->then(function ($response) { + // Sended the request and received the response - // Writing the current quarter identifier into the quarter buffer - $quarter = $value->quarter; + // Initializing the user journal + $journal = json_decode((string) $response->getBody()); - // Stopping iterating over study year quarters - break; - } + if (!empty($journal)) { + // Initialized the user journal + + // Exit (success) + return $journal; + } else { + // Not initialized the user journal + + // Exit (fail) + return false; + } + })->wait(); + } else { + // Not initialized number of the quarter + + // Exit (fail) + return false; } - - // Initializing the request to the user journal API - $request = new request( - 'GET', - static::JOURNAL . "?grade=$grade&quarter=$quarter&year_id=$year->id&user_id=$identifier&token=$this->token" - ); - - return $this->browser->sendAsync($request) - ->then(function ($response) { - // Sended the request and received the response - - // Initializing the user journal - $journal = json_decode((string) $response->getBody()); - - if (!empty($journal)) { - // Initialized the user journal - - // Exit (success) - return $journal; - } else { - // Not initialized the user journal - - // Exit (fail) - return false; - } - })->wait(); } else { // Not initialized the moscow time @@ -922,4 +904,53 @@ final class interneturok extends core return 0; } } + + /** + * Week + * + * Generate datetime by number of the week (by 6 days) + * + * @param int $number Number of the week + * + * @return datetime The date + */ + public static function week(int $number = 2): datetime + { + // Initializing the date + $date = new datetime('first day of september last year'); + + // Normalizing number of the week + if ($number < 2) $number = 2; + else if ($number > 37) $number = 37; + + // Offsetting number of the week for calculating + $number = $number - 1; + + // Calculating days for offsetting the date + $days = 6 * $number; + + // Exit (success) + return $date->modify("+$days days +$number days"); + } + + /** + * Quarter + * + * Generate number of the quarter by number of the week + * + * @param int $number Number of the week + * + * @return int|false Number of the quarter + */ + public static function quarter(int $number = 2): int|false + { + // Exit (success) + return match (true) { + $number >= 2 && $number <= 9 => 1, + $number >= 11 && $number <= 18 => 2, + $number >= 21 && $number <= 30 => 3, + $number >= 32 && $number <= 38 => 4, + default => false + }; + } } diff --git a/mirzaev/parser_from_interneturok/system/models/telegram/parser.php b/mirzaev/parser_from_interneturok/system/models/telegram/parser.php index fc23f88..f002efa 100644 --- a/mirzaev/parser_from_interneturok/system/models/telegram/parser.php +++ b/mirzaev/parser_from_interneturok/system/models/telegram/parser.php @@ -18,7 +18,8 @@ use Zanzara\Context as context, use function React\Async\await; // Built-in libraries -use Exception as exception; +use Exception as exception, + DateTime as datetime; /** * Telegram shcool subjects parser @@ -52,7 +53,9 @@ final class parser extends core // Initializing the parsing target buffer $target = [ 'subject' => $subject, - 'grade' => null + 'grade' => null, + 'date' => null, + 'week' => null ]; // Writing to the telegram user buffer @@ -114,7 +117,7 @@ final class parser extends core /** * Grade * - * Write grade to the buffer and request confirmation for starting the parsing process + * Write grade to the buffer and request date * * @param context $context Request data from Telegram * @param int $grade The grade @@ -134,6 +137,207 @@ final class parser extends core // Initializing the grade $target['grade'] = $grade; + // Writing to the telegram user buffer + $context->setUserDataItem(static::PROCESS, $target) + ->then(function () use ($context, $target) { + // Writed to the telegram user buffer + + // Sending the message + $context->sendMessage( + '📅 *Выберите номер недели*', + [ + 'reply_markup' => [ + 'inline_keyboard' => [ + [ + [ + 'text' => '2', + 'callback_data' => 'week_2' + ], + [ + 'text' => '3', + 'callback_data' => 'week_3' + ], + [ + 'text' => '4', + 'callback_data' => 'week_4' + ], + [ + 'text' => '5', + 'callback_data' => 'week_5' + ], + [ + 'text' => '6', + 'callback_data' => 'week_6' + ], + [ + 'text' => '7', + 'callback_data' => 'week_7' + ], + [ + 'text' => '8', + 'callback_data' => 'week_8' + ], + [ + 'text' => '9', + 'callback_data' => 'week_9' + ] + ], + [ + [ + 'text' => '11', + 'callback_data' => 'week_11' + ], + [ + 'text' => '12', + 'callback_data' => 'week_12' + ], + [ + 'text' => '13', + 'callback_data' => 'week_13' + ], + [ + 'text' => '14', + 'callback_data' => 'week_14' + ], + [ + 'text' => '15', + 'callback_data' => 'week_15' + ], + [ + 'text' => '16', + 'callback_data' => 'week_16' + ], + [ + 'text' => '17', + 'callback_data' => 'week_17' + ], + [ + 'text' => '18', + 'callback_data' => 'week_18' + ] + ], + [ + [ + 'text' => '21', + 'callback_data' => 'week_21' + ], + [ + 'text' => '22', + 'callback_data' => 'week_22' + ], + [ + 'text' => '23', + 'callback_data' => 'week_23' + ], + [ + 'text' => '24', + 'callback_data' => 'week_24' + ], + [ + 'text' => '25', + 'callback_data' => 'week_25' + ] + ], + [ + [ + 'text' => '26', + 'callback_data' => 'week_26' + ], + [ + 'text' => '27', + 'callback_data' => 'week_27' + ], + [ + 'text' => '28', + 'callback_data' => 'week_28' + ], + [ + 'text' => '29', + 'callback_data' => 'week_29' + ], + [ + 'text' => '30', + 'callback_data' => 'week_30' + ] + ], + [ + [ + 'text' => '32', + 'callback_data' => 'week_32' + ], + [ + 'text' => '33', + 'callback_data' => 'week_33' + ], + [ + 'text' => '34', + 'callback_data' => 'week_34' + ], + [ + 'text' => '35', + 'callback_data' => 'week_35' + ], + [ + 'text' => '36', + 'callback_data' => 'week_36' + ], + [ + 'text' => '37', + 'callback_data' => 'week_37' + ], + [ + 'text' => '38', + 'callback_data' => 'week_38' + ] + ] + ], + 'disable_notification' => true, + 'remove_keyboard' => true + ] + ] + ); + }); + } else { + // Not initialized the parsing process + + // Sending the message + $context->sendMessage('⚠️ *Не запущен процесс генерации парсинга*') + ->then(function (message $message) use ($context) { + // Sended the message + + // Sending the menu with subjects + commands::menu($context); + }); + } + }); + } + + /** + * Date + * + * Write date and request confirmation for starting the parsing process + * + * @param context $context Request data from Telegram + * @param int $week Number of the week + * + * @return void + */ + public static function date(context $context, int $week): void + { + // Reading from the telegram user buffer + $context->getUserDataItem(static::PROCESS) + ->then(function (?array $target) use ($context, $week) { + // Readed from the telegram user buffer + + if ($target) { + // Initialized the parsing process + + // Initializing the week + $target['week'] = $week; + + // Initializing the date + $target['date'] = interneturok::week($week); + // Writing to the telegram user buffer $context->setUserDataItem(static::PROCESS, $target) ->then(function () use ($context, $target) { @@ -145,14 +349,21 @@ final class parser extends core // Initializing the grade $grade = $target['grade']; + // Reinigitlizing the date + $date = $target['date']->format('Y\\\.m\\\.d'); + + // Inigitlizing number the week + $week = $target['week']; + // Sending the message $context->sendMessage( << [ 'inline_keyboard' => [ @@ -201,51 +412,71 @@ final class parser extends core if ($target) { // Initialized the parsing process - // Initializing variables for the parsing process - $subject = $target['subject']; - $grade = $target['grade']; + // Sending the message + $context->sendMessage('⚡️ *Запущен процесс парсинга домашнего задания*') + ->then(function (message $message) use ($context, $target) { + // Sended the message - // Initializing the parser - $parser = new interneturok(); + // Initializing variables for the parsing process + $subject = $target['subject']; + $grade = $target['grade']; + $date = $target['date']; + $week = $target['week']; - try { - // Parsing homework files - $files = $parser->parse(subject: $subject, grade: $grade, waiting: 3); + // Initializing the parser + $parser = new interneturok(); - if (!empty($files)) { - // Initialized homework files + try { + // Parsing homework + $homework = $parser->parse(subject: $subject, grade: $grade, date: $date, week: $week, waiting: 2); - foreach ($files as $file) { - // Iterating over files in the storage + if (!empty($homework)) { + // Initialized homework - // Sending the file - await($context->sendDocument(new file_input($file))); + foreach ($homework['files'] as $file) { + // Iterating over homework files in the storage + + // Sending the file + await($context->sendDocument(new file_input($file))); + } + + if ($homework['downloads']['year'] <= 100) { + // The homework was downloadsd 100 or less times for 1 last year + + // Sending the message + $context->sendMessage('Домашнее задание было скачано *' . $homework['downloads']['year'] . ' раз* за последний год'); + } else { + // The homework was downloaded more than 100 times for 1 last year + + // Sending the message + $context->sendMessage('Домашнее задание было скачано *более чем 100 раз*) за последний год'); + } + + // Deleting in the telegram user buffer + $context->deleteUserDataItem(static::PROCESS); + } else { + // Not initialized homework files + + // Sending the message + $context->sendMessage('❌ *Не найдены домашние задания*') + ->then(function (message $message) use ($context) { + // Sended the message + + // Sending the menu with subjects + commands::menu($context); + }); + } + } catch (exception $exception) { + // Sending the message + $context->sendMessage('⚠️ *Произошла ошибка при обработке домашних заданий*') + ->then(function (message $message) use ($context) { + // Sended the message + + // Sending the menu with subjects + commands::menu($context); + }); } - - // Deleting in the telegram user buffer - $context->deleteUserDataItem(static::PROCESS); - } else { - // Not initialized homework files - - // Sending the message - $context->sendMessage('❌ *Не найдены домашние задания*') - ->then(function (message $message) use ($context) { - // Sended the message - - // Sending the menu with subjects - commands::menu($context); - }); - } - } catch (exception $exception) { - // Sending the message - $context->sendMessage('⚠️ *Произошла ошибка при обработке домашних заданий*') - ->then(function (message $message) use ($context) { - // Sended the message - - // Sending the menu with subjects - commands::menu($context); - }); - } + }); } else { // Not initialized the parsing process diff --git a/mirzaev/parser_from_interneturok/system/public/telegram.php b/mirzaev/parser_from_interneturok/system/public/telegram.php index 50f621b..7b15d3a 100755 --- a/mirzaev/parser_from_interneturok/system/public/telegram.php +++ b/mirzaev/parser_from_interneturok/system/public/telegram.php @@ -80,6 +80,34 @@ for ($i = 4; $i <= 11; ++$i) { $robot->onCbQueryData(["grade_$i"], fn(context $context) => parser::grade(context: $context, grade: $i)); } +for ($i = 2; $i <= 9; ++$i) { + // Generating buttons from 2 to 9 numbers of weeks + + // Initializing numbers of weeks buttons + $robot->onCbQueryData(["week_$i"], fn(context $context) => parser::date(context: $context, week: $i)); +} + +for ($i = 11; $i <= 18; ++$i) { + // Generating buttons from 11 to 18 numbers of weeks + + // Initializing numbers of weeks buttons + $robot->onCbQueryData(["week_$i"], fn(context $context) => parser::date(context: $context, week: $i)); +} + +for ($i = 21; $i <= 30; ++$i) { + // Generating buttons from 21 to 30 numbers of weeks + + // Initializing numbers of weeks buttons + $robot->onCbQueryData(["week_$i"], fn(context $context) => parser::date(context: $context, week: $i)); +} + +for ($i = 32; $i <= 38; ++$i) { + // Generating buttons from 32 to 38 numbers of weeks + + // Initializing numbers of weeks buttons + $robot->onCbQueryData(["week_$i"], fn(context $context) => parser::date(context: $context, week: $i)); +} + // Initializing the parse button $robot->onCbQueryData(['parse'], [parser::class, 'parse']);