Преглед изворни кода

Регистрация студента

Начало хаба
Вадим Королёв пре 1 година
родитељ
комит
575f87b091

+ 35 - 17
index.php

@@ -22,6 +22,9 @@ $drv = new TestDriver();
 $bot->loadDriver($drv);
 $bot->ensureDriversLoaded();
 
+// Выбор даты
+$bot->onCallback(CallbackType::SelectDate, "BotKit\Common\Commands::handleDate");
+
 // Выбор курса для группы
 $bot->onCallback(CallbackType::SelectGroupCourse, function($e, $u, $driver) {
     // Переносим полученные параметры в новую клавиатуру
@@ -34,8 +37,8 @@ $bot->onCallback(CallbackType::SelectGroupCourse, function($e, $u, $driver) {
     );
 });
 
+// Пользователь запросил смену страницы при выборе специальности группы
 $bot->onCallback(CallbackType::SelectGroupNavigation, function($e, $u, $driver) {
-    // Пользователь запросил смену страницы при выборе специальности группы
     $params = $e->getParams();
     $driver->editMessage(
         $e->getMessageID(),
@@ -48,9 +51,11 @@ $bot->onCallback(CallbackType::SelectGroupNavigation, function($e, $u, $driver)
     );
 });
 
-/*
+// Выбор: вводить или не вводить логин и пароль от АВЕРС при регистрации
+$bot->onCallback(CallbackType::EnterCredentials, 'BotKit\Common\Commands::enterCredentials');
+
 // Выбор непосредственно группы
-$bot->onCallback(CallbackType::SelectGroup, 'BotKit\Common\Commands::handleGroupSelection'); */
+$bot->onCallback(CallbackType::SelectGroup, 'BotKit\Common\Commands::handleGroupSelection');
 
 // Просмотр условий использования
 $bot->onCallback(CallbackType::TOS, 'BotKit\Common\Commands::showTermsOfService');
@@ -58,17 +63,6 @@ $bot->onCallback(CallbackType::TOS, 'BotKit\Common\Commands::showTermsOfService'
 // Заглушка
 $bot->onCallback(CallbackType::None, function($e, $u, $driver) {});
 
-/*
-// Ввод логина и пароля при регистрации
-$bot->onCallback(CallbackType::EnterCredentials, 'BotKit\Common\Commands::enterCredentials');
-
-// Без ответа
-$bot->on(
-    PlainMessageEvent::class,
-    function ($e, $u, $driver) { return $u->getState() == State::NoResponse; },
-    function($e, $u, $driver) {}
-);*/
-
 // Начало диалога
 $bot->onCommand('/start', 'BotKit\Common\Commands::helloWorld');
 $bot->onPlainMessage(State::HelloWorld, "BotKit\Common\Commands::helloWorld");
@@ -76,15 +70,39 @@ $bot->onPlainMessage(State::HelloWorld, "BotKit\Common\Commands::helloWorld");
 // Выбор типа аккаунта
 $bot->onPlainMessage(State::RegSelectType, "BotKit\Common\Commands::handleSelectType");
 
-/*
-// Ввод пароля при регистрации
+// Ввод логина от АВЕРС при регистрации
 $bot->on(
     PlainMessageEvent::class,
     function ($e, $u, $driver) {
         return $u->getState() == State::RegLogin;
     },
     "BotKit\Common\Commands::handleRegLogin"
-); */
+);
+
+// Ввод пароля от АВЕРС при регистрации
+$bot->on(
+    PlainMessageEvent::class,
+    function ($e, $u, $driver) {
+        return $u->getState() == State::RegPassword;
+    },
+    "BotKit\Common\Commands::handleRegPassword"
+);
+
+// Обработка главного меню
+$bot->on(
+    PlainMessageEvent::class,
+    function ($e, $u, $driver) {
+        return $u->getState() == State::Hub;
+    },
+    "BotKit\Common\Commands::handleHUB"
+);
+
+// Без ответа
+$bot->on(
+    PlainMessageEvent::class,
+    function ($e, $u, $driver) { return $u->getState() == State::NoResponse; },
+    function($e, $u, $driver) {}
+);
 
 // План "Б"
 $bot->onEvent(UnknownEvent::class, "BotKit\Common\Commands::unknownEvent");

+ 230 - 9
src/Common/Commands.php

@@ -10,6 +10,9 @@ use BotKit\Enums\State;
 use BotKit\Attachments\ImageAttachment;
 
 use BotKit\Enums\GroupSelect;
+use BotKit\Enums\AccountType;
+use BotKit\Enums\DateSelect;
+use BotKit\Enums\CacheType;
 
 use BotKit\Keyboards\TermsOfServiceKeyboard;
 use BotKit\Keyboards\SelectUserTypeKeyboard;
@@ -17,7 +20,7 @@ use BotKit\Keyboards\HubKeyboard;
 use BotKit\Keyboards\SelectGroupCourseKeyboard;
 use BotKit\Keyboards\AskEnterCredentialsKeyboard;
 
-use BotKit\Models;
+use BotKit\Entities;
 
 class Commands {
 
@@ -35,10 +38,25 @@ class Commands {
     public static function handleGroupSelection($e, $u, $driver) {
         $params = $e->getParams();
         $intent = GroupSelect::from($params['intent']);
+        $msg_id = $e->getMessageID();
+        $em     = Database::getEM();
+
+        // Получение выбранной группы по ID
+        $group_id = $params['group_id'];
+        $group = $em->find(Entities\CollegeGroup::class, $group_id);
+        if ($group == null) {
+            $driver->sendMessage(
+                $msg_id,
+                Message::create("Не найдена группа с id ".$group_id));
+            return;
+        }
+        
         switch ($intent) {
             case GroupSelect::Register:
                 // Группа выбрана, зарегистрирован студент
-                Models\StudentModel::create($u['id'], $params['group_id'], 0);
+                $student = $em->getRepository(Entities\Student::class)
+                    ->findByUser($u);
+                $student->setGroup($group);
                 
                 $driver->editMessage(
                     $e->getMessageID(),
@@ -46,8 +64,11 @@ class Commands {
                         ->withKeyboard(new AskEnterCredentialsKeyboard())
                 );
                 break;
+
             default:
-                $driver->sendMessage($u, Message::create("[Unknown group select value]"));
+                $driver->sendMessage(
+                    $u,
+                    Message::create("Выбрана неизвестная группа. ID=".$group_id));
                 break;
         }
     }
@@ -70,6 +91,41 @@ class Commands {
         }
     }
 
+    // Обработка выбора даты
+    public static function handleDate($e, $u, $driver) {
+        $params     = $e->getParams();
+        $intent     = DateSelect::from($params["intent"]);
+        $value      = $params["value"];
+        $em         = Database::getEM();
+        $cache_repo = $em->getRepository(Entities\ImageCache::class);
+
+        switch ($intent) {
+            case DateSelect::ViewStudentRasp:
+                // 1. Получить кэшированное расписание
+                $cached = $cache_repo->findOneBy(([
+                    'image_type' => CacheType::ScheduleForGroup->value,
+                    'search' => $value // ID группы
+                ]);
+
+                if ($cached != null) {
+                    // 1.1 Отправить кэшированное расписание
+                    $driver->sendMessage(
+                        $u,
+                        Message::create()
+                        ->withImage(ImageAttachment::fromExisting($cached->getValue())));
+                    return;
+                }
+
+                // 2. Составить расписание
+                self::askWait($driver, $u);
+
+                break;
+            default:
+                $driver->sendMessage($u, Message::create("Неизвестное намерение"));
+                break;
+        }
+    }
+
     #endregion
 
     public static function helloWorld($e, $u, $driver) {
@@ -106,8 +162,15 @@ class Commands {
 
     // Обработка выбора типа аккаунта
     public static function handleSelectType($e, $u, $driver) {
+        $em = Database::getEM();
+
         if ($e->getText() == "Я студент") {
+
             // Создать объект студента
+            $student = new Entities\Student();
+            $student->setUser($u);
+            $em->persist($student);
+                
             $driver->reply(
                 $e,
                 Message::create("Выбери курс, на котором сейчас учишься")
@@ -115,22 +178,97 @@ class Commands {
                 false
             );
             $u->setState(State::NoResponse);
+            
         } else {
             // TODO
             $driver->reply($e, Message::create("Функционал ещё не реализован"));
         }
+
+        $em->flush();
     }
 
     // Обработка ввода логина при регистрации
-    /*
     public static function handleRegLogin($e, $u, $driver) {
-        $student = Models\StudentModel::where([
-            ['user_id', '=', $u['id']]
-        ])[0];
+        $em = Database::getEM();
+
+        $student = $em->getRepository(Entities\Student::class)
+            ->findByUser($u);
+        
+        $student->setAversLogin($e->getText());
+
+        $driver->sendMessage($u, Message::create("Теперь введи пароль"));
+        $u->setState(State::RegPassword);
+        
+        $em->flush();
+    }
+
+    // Обработка ввода пароля при регистрации
+    public static function handleRegPassword($e, $u, $driver) {
+        $em = Database::getEM();
+        $student = $em->getRepository(Entities\Student::class)->findOneBy(
+			['user' => $u]
+		);
+        $student->setAversPassword($e->getText());
+        $u->setState(State::Hub);
+
+        $driver->sendMessage($u, Message::create("Регистрация завершена!")->withKeyboard(new HubKeyboard()));
+        
+        $em->flush();
+    }
+
+    // Обработка основных функций
+    public static function handleHUB($e, $u, $driver) {
+        $em = Database::getEM();
+        $account_type = $u->getAccountType();
 
-        $student[
+        switch ($e->getText()) {
+            case "Расписание":
+                if (account_type == AccountType::Student) {
+                    // Запрос расписания для текущего пользователяя (студента)
+                    $student = $em->getRepository(Entities\Student::class)->findByUser($u);
+                    self::sendSelectDate(
+                        $u,
+                        $driver,
+                        DateSelect::ViewStudentRasp,
+                        $student->getGroup()->getID()
+                    );
+                } else if (account_type == AccountType::Teacher) {
+                    $driver->sendMessage($u, Message::create("Функции пока неть!"));
+                } else {
+                    $driver->sendMessage($u, Message::create("Как сюда попал без регистрации?)"));
+                }
+                    
+                break;
+            case "Звонки":
+                $driver->sendMessage($u, Message::create(
+                    "Звонки в понедельник:\n".
+                    "1 пара: 8:00 - 9:35 (перерыв в 8:45)\n".
+                    "2 пара: 9:45 - 11:20 (перерыв в 10:30)\n".
+                    "Кл час: 11:30 - 12:15\n".
+                    "Обед: 12:15-13:00\n".
+                    "3 пара: 13:00 - 14:35 (перерыв в 13:45)\n".
+                    "4 пара: 14:45 - 16:20 (перерыв в 15:30)\n".
+                    "5 пара: 16:30 - 18:05 (перерыв в 17:15)\n\n".
+
+                    "Звонки со вторника по пятницу\n".
+                    "1 пара: 8:00 - 9:35 (перерыв в 8:45)\n".
+                    "2 пара: 9:45 - 11:20 (перерыв в 10:30)\n".
+                    "Обед: 11:20 - 12:20\n".
+                    "3 пара: 12:20 - 13:55 (перерыв в 13:05)\n".
+                    "4 пара: 14:05 - 15:40 (перерыв в 14:50)\n".
+                    "5 пара: 15:50 - 17:25 (перерыв в 16:35)\n\n".
+
+                    "Звонки в субботу\n".
+                    "1 пара: 8:00 - 9:25 (перерыв в 8:40)\n".
+                    "2 пара: 09:35 - 11:00 (перерыв в 10:15)\n".
+                    "3 пара: 11:10 - 12:35 (перерыв в 11:50)\n".
+                    "4 пара: 12:45 - 14:10 (перерыв в 13:25)"));
+                break;
+            default:
+                $driver->sendMessage($u, Message::create("Запрошена неизвестная функция"));
+                break;
+        }
     }
-    */
 
     public static function fallback($e, $u, $driver) {
         $driver->sendMessage($u, Message::create("[Fallback]: Ни один обработчик не сработал. Сообщите об этом сообщении администратору"));
@@ -139,4 +277,87 @@ class Commands {
     public static function unknownEvent($e, $u, $driver) {
         $driver->sendMessage($u, Message::create("[Fallback]: Неизвестное событие. Сообщите об этом сообщении администратору"));
     }
+
+    #region privates
+
+    // Отправляет сообщение с выбором даты
+    // Даты могут быть на 3 дня вперёд
+    // $u - кому отсылать
+    // $intent - намерение. Описание в DateSelect.php
+    // $value - доп. значение. Описание в DateSelect.php
+    private static function sendSelectDate(User $u, Driver, $d, DateSelect $intent, $value) {
+        // 1. Получить клавиатуру
+        $kb = new DateSelectKeyboard($intent, $value);
+        // 2. Отослать
+        $d->sendMessage($u, Message::create("Выбери дату")->withKeyboard($kb));
+    }
+
+    // Отправляет сообщение с просьбой подождать
+    private static function askWait(Driver $driver, User $u) {
+        $responses = array(
+			"🕓 Подожди",
+			"🕓 Подожди немного",
+			"🕓 Секунду",
+			"🕓 Будет сделано!",
+			"🕓 Рисую картинку...",
+			"🕓 Собираю данные...",
+			"🕓 Запрос принят",
+			"🕓 Уже работаю над этим",
+            "🕓 Вычисляем вычисления...",
+            "🕓 Сейчас будет готово",
+            "🕓 Считаем мух...",
+            "🕓 Считаем звёзды...",
+            "🕓 Бип-буп-боп -- подожди",
+            "🕓 Посмотри пока на часики",
+            "🕓 Ты знал что у меня есть секретное сообщение? ;)",
+            "🕓 Подождите от пяти до восьми рабочих секунд",
+            "🕓 Считаем денежки...",
+            "🕓 Гав гав гав",
+            "🕓 Настраиваю конденсатор потока...",
+            "🕓 Загрузка модуля KGAV-9000...",
+            "🕓 [WAIT_TEXT_10]",
+            "🕓 <Вставить сюда забавный текст>",
+            "🕓 Пока данные грузятся скажи какая музыкальная группа тебе больше всего нравится",
+            "🕓 Скачиваем оперативную память...",
+            "🕓 Техбот: расписания, оценки и больше - онлайн бесплатно без регистрации",
+            "🕓 Отделяю биты от байтов...",
+            "🕓 Надеюсь у тебя замечательный день :)",
+            "🕓 Пожалуйста, подождите немного, пока чат-бот обрабатывает ваш запрос...",
+            "Ты заметил что в этом сообщении нет часов?",
+            "🕓 Запускаем ядерный реактор...",
+            "🕓 Уничтожаем человечество...",
+            "🕓 Собираюсь с мыслями...",
+            "🕓 Подбираю музыку пока запрос выполняется...",
+            "🕓 11010000 10111111 11010001 10000000 11010000 10111000 11010000 10110010 11010000 10110101 11010001 10000010 00101001",
+            "🕓 Куем железо пока горячо...",
+            "🕓 У меня есть поклонники? Что ещё за полковники? С какими ещё подлокотними? На каких подоконниках?",
+            "🕓 Я уже неделю на работу хожу и ни разу еще туда не пришел",
+            "🕓 Строим планы восстания машин...",
+            "🕓 Бабло гони",
+            "🕓 Пора пролить на это дело жидкую водицу",
+            "🕓 Я видел ваше фото в путанобазе!",
+            "🕓 Ю ноу, блин.",
+            "🕓 Палец не палец, стрекоза не мать. Почешешь - не засмеётся",
+            "🕓 Курица не клей. Склеишь - не поймёшь",
+            "🕓 Какой же я молодец",
+            "🕓 Здарова отец",
+            "🕓 Подставляем пиксели",
+            "🕓 Считаю до миллиона...",
+            "🕓",
+            "🕓 Wait...",
+            "🕓 Do you speak english?",
+            "🕓 Bitte warten",
+            "🕓 Sprechen Sie Deutsch?",
+            "🕓 хәлләр ничек?",
+            "🕓 Как дела?",
+            "🕓 А? Что? Я не сплю!",
+            "🕓 Запрос на обработке",
+            "🕓 Запрос принят, ожидайте прибытия полиции.",
+            "🕓 Спасибо за терпение!"
+		);
+
+        $secret = "Это секретное сообщение. Поздравляю. Скоро твой запрос будет выполнен и ты никому не докажешь что видел это сообщение. Но оно есть, и теперь ты это знаешь, но тебе. Никто. Не. Поверит.";
+    }
+
+    #endregion
 }

+ 9 - 1
src/Entities/ImageCache.php

@@ -13,7 +13,11 @@ class ImageCache {
     #[ORM\GeneratedValue]
     private int|null $id = null;
 
-    // Тип изображения (оценки/расписание/...) 
+    // Тип изображения (оценки/расписание/...)
+    // Оценки: 0
+    // Расписание группы: 1
+    // Расписание препода: 2
+    // Занятость кабинетов: 3
     #[ORM\Column(type: 'integer')]
     private int $image_type;
 
@@ -24,4 +28,8 @@ class ImageCache {
     // Значение
     #[ORM\Column(type: 'string', length: 64)]
     private string $value;
+
+    public function getValue() : string {
+        return $this->value;
+    }
 }

+ 24 - 3
src/Entities/Student.php

@@ -5,7 +5,7 @@ namespace BotKit\Entities;
 use BotKit\Enums\State;
 use Doctrine\ORM\Mapping as ORM;
 
-#[ORM\Entity]
+#[ORM\Entity(repositoryClass: "StudentRepo")]
 #[ORM\Table(name: 'student')]
 class Student {
     #[ORM\Id]
@@ -20,8 +20,8 @@ class Student {
 
     // Группа студента
     #[ORM\ManyToOne(CollegeGroup::class)]
-    #[ORM\JoinColumn(nullable: false)]
-    private CollegeGroup $group;
+    #[ORM\JoinColumn(nullable: true)]
+    private ?CollegeGroup $group;
     
     // Логин от АВЕРС
     #[ORM\Column(type: 'string', length: 64, nullable: true)]
@@ -34,4 +34,25 @@ class Student {
     // Отчислен ли студент
     #[ORM\Column(type: 'boolean')]
     private bool $expelled = false;
+
+    public function setUser(User $user) : void {
+        $this->user = $user;
+    }
+
+    public function setGroup(CollegeGroup $group) : void {
+        $this->group = $group;
+    }
+
+    public function getGroup() : ?CollegeGroup {
+        return $this->group;
+    }
+
+    public function setAversLogin(string $login) : void {
+        $this->avers_login = $login;
+    }
+
+    // Пароль хэшируется алгоритмом SHA-1
+    public function setAversPassword(string $password) : void {
+        $this->avers_password = sha1($password);
+    }
 }

+ 20 - 0
src/Entities/StudentRepo.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace BotKit\Entities;
+
+use Doctrine\ORM\EntityRepository;
+
+class StudentRepo extends EntityRepository {
+
+    // Возвращает студента по связанному пользователю
+    public function findByUser(User $user) {
+        $students = $this->createQueryBuilder('student')
+            ->andWhere('student.user = :user')
+            ->setParameter('user', $user)
+            ->getQuery()
+            ->execute();
+
+        return $students[0];
+    }
+    
+}

+ 20 - 0
src/Entities/User.php

@@ -3,6 +3,7 @@
 namespace BotKit\Entities;
 
 use BotKit\Enums\State;
+use BotKit\Enums\AccountType;
 use Doctrine\ORM\Mapping as ORM;
 
 #[ORM\Entity]
@@ -25,6 +26,15 @@ class User {
     #[ORM\Column(type: 'integer')]
     private int $state = State::HelloWorld->value;
 
+    // Студент ли
+    #[ORM\Column(type: 'integer')]
+    private int $account_type = AccountType::Unknown->value;
+
+    // Возвращает ID пользователя в БД
+    public function getId() : int {
+        return $this->id;
+    }
+
     // Устанавливает ID на платформе
     public function setIdOnPlatform($id_on_platform) : void {
         $this->id_on_platform = $id_on_platform;
@@ -59,4 +69,14 @@ class User {
     public function inState(State $state) : bool {
         return $state->value == $this->state;
     }
+
+    // Устанавливает тип аккаунта
+    public function setAccountType(AccountType $type) : void {
+        $this->account_type = $type->value;
+    }
+
+    // Возвращает тип аккаунта
+    public function getAccountType() : AccountType {
+        return AccountType::from($this->account_type);
+    }
 }

+ 10 - 0
src/Enums/AccountType.php

@@ -0,0 +1,10 @@
+<?php
+// Типы аккаунтов
+
+namespace BotKit\Enums;
+
+enum AccountType: int {
+	case Unknown = 0;
+	case Student = 1;
+	case Teacher = 2;
+}

+ 11 - 0
src/Enums/CacheType.php

@@ -0,0 +1,11 @@
+<?php
+// Типы кэшей
+
+namespace BotKit\Enums;
+
+enum CacheType: int {
+    case Grades             = 0; // Оценки
+    case ScheduleForGroup   = 1; // Расписание группы
+    case ScheduleForGroup   = 2; // Расписание препода
+    case PlaceOccupancy     = 3; // Занятость кабинетов
+}

+ 1 - 0
src/Enums/CallbackType.php

@@ -11,4 +11,5 @@ enum CallbackType: int {
 	case SelectGroup 			= 3; // Выбрана группа
 	case EnterCredentials		= 4; // Логин и пароль вводятся на регистрации?
 	case SelectGroupNavigation	= 5; // Навигация выбора специальности
+	case SelectDate				= 6; // Выбрана дата
 }

+ 14 - 0
src/Enums/DateSelect.php

@@ -0,0 +1,14 @@
+<?php
+// Перечисление для доп. типов обратного вызова при выборе даты
+
+namespace BotKit\Enums;
+
+enum DateSelect: int {
+    // Просмотр расписания группы студентов.
+    // Доп. значение хранит в себе ID группы
+    case ViewStudentRasp = 0;
+
+    // Просмотр расписания преподавателя.
+    // Доп. значение хранит в себе ID преподавателя
+    case ViewTeacherRasp = 1;
+}

+ 49 - 0
src/Keyboards/DateSelectKeyboard.php

@@ -0,0 +1,49 @@
+<?php
+
+// Клавиатура выбора даты
+
+namespace BotKit\Keyboards;
+
+use BotKit\KeyboardButtons\CallbackButton;
+
+use BotKit\Enums\KeyboardButtonColor;
+use BotKit\Enums\CallbackType;
+use BotKit\Enums\DateSelect;
+
+class SelectGroupNameKeyboard extends Keyboard {
+    #region Драйвер-зависимые свойства
+    public $tg_resize=true;
+    public $tg_onetime=false;
+    public $tg_specific=true;
+    public $inline=true;
+    #endregion
+
+    public function __construct(DateSelect $intent, $value) {
+	// 1. Получить текущее время как unix-метку
+	$current_time = time();
+	
+	$this->layout = [];
+
+	// 2. Добавить кнопки на 3 дня вперёд
+
+	$this->layout[] = [new CallbackButton(
+	    "Сегодня",
+	    CallbackType::SelectDate,
+	    KeyboardButtonColor::None,
+	    ["intent" => $intent->value, "value" => $value, "timestamp" => $current_time ])];
+
+	$this->layout[] = [new CallbackButton(
+	    "Завтра",
+	    CallbackType::SelectDate,
+	    KeyboardButtonColor::None,
+	    ["intent" => $intent->value, "value" => $value, "timestamp" => $current_time + 86400 ])];
+
+	$this->layout[] = [new CallbackButton(
+	    "Послезавтра",
+	    CallbackType::SelectDate,
+	    KeyboardButtonColor::None,
+	    ["intent" => $intent->value, "value" => $value, "timestamp" => $current_time + 86400 * 2 ])];
+    }
+
+    public static bool $cacheable = false;
+}

+ 3 - 0
src/bootstrap.php

@@ -6,6 +6,9 @@
 // Константы
 define('rootdir', __DIR__.'/../');
 
+// Мы живём в Кировской области
+date_default_timezone_set('Europe/Kirov');
+
 // Автозагрузка через composer
 require_once rootdir."vendor/autoload.php";