Вадим Королёв 1 рік тому
батько
коміт
57c4b613bf

+ 32 - 6
index.php

@@ -6,23 +6,49 @@ namespace BotKit;
 require_once __DIR__.'/src/bootstrap.php';
 use BotKit\Common\Bot;
 use BotKit\Common\Commands;
-use BotKit\Drivers\TgBotDriver;
+use BotKit\Drivers\TestDriver;
 use BotKit\Events\PlainMessageEvent;
 use BotKit\Events\MemberJoinedEvent;
 use BotKit\Events\MemberLeftEvent;
-
-$tgdriver = new TgBotDriver($_ENV['TELEGRAMORG_TOKEN']);
+use BotKit\Enums\State;
+use BotKit\Enums\CallbackType;
 
 $bot = new Bot();
-$bot->loadDriver($tgdriver);
+$bot->loadDriver(new TestDriver());
 $bot->ensureDriversLoaded();
 
+// Нажатие на кнопку условий использования
+//~ $bot->onCallback(
+    //~ CallbackType::TOS,
+    //~ "BotKit\Common\Commands::showTermsOfService"
+//~ );
+
+// Команда помощь
+$bot->onCommand(
+    "/help",
+    "BotKit\Common\Commands::help"
+);
+
+// Помощь по теме
+$bot->onCommand(
+    "/help {topic}",
+    "BotKit\Common\Commands::helpTopic"
+);
+
+$bot->on(
+    PlainMessageEvent::class,
+    function ($e, $u, $driver) {
+        $result = $u->getState() == State::HelloWorld;
+        return $result;
+    },
+    "BotKit\Common\Commands::helloWorld"
+);
 $bot->on(
     PlainMessageEvent::class,
     function ($e, $u, $driver) {
-        return true;
+        return $u->getState() == State::Registering;
     },
-    "BotKit\Common\Commands::echoMessage"
+    "BotKit\Common\Commands::registered"
 );
 
 $bot->onEvent(

+ 109 - 27
src/Common/Bot.php

@@ -5,6 +5,7 @@ namespace BotKit\Common;
 
 use BotKit\Events\Event;
 use BotKit\Models\UserModel;
+use BotKit\Events\PlainMessageEvent;
 
 class Bot {
 
@@ -24,7 +25,6 @@ class Bot {
         if ($driver->forThis()) {
             $this->driver_loaded = true;
             $this->driver = $driver;
-            $this->event = $driver->getEvent();
         }
     }
 
@@ -35,6 +35,36 @@ class Bot {
         if ($this->driver_loaded == false) {
             throw new \Exception("Bot has no loaded drivers");
         }
+        $this->driver->onSelected();
+        $this->event = $this->driver->getEvent();
+    }
+
+    // Общая процедура обработки запроса
+    // $callback - то что будет выполняться
+    // $params - доп. параметры для $callback
+    private function processRequest(callable $callback, array $params) : void {
+        $this->driver->onProcessStart();
+        
+        $user = $this->event->getUser();
+        $callback_params = [
+            'e' => $this->event,
+            'u' => $user,
+            'driver' => $this->driver
+        ];
+        $final_params = array_merge($callback_params, $params);
+        call_user_func_array($callback, $final_params);
+
+        // Сохранение пользователя...
+        if ($user->hasChanged()) {
+            // Бот изменил пользователя, сохраняем его данные
+            UserModel::updateObject($user->getDbObject());
+            $this->driver->onUserSave($user);
+        }
+
+        // Завершение работы драйвера...
+        $this->driver->onProcessEnd();
+
+        exit();
     }
 
     // Подключает обработчик события
@@ -65,25 +95,7 @@ class Bot {
             // Обрабатываемое событие - не для этого обработчика
             return;
         }
-
-        // Вызов обработчика
-        $user = $this->event->getUser();
-        $callback_params = [
-            'e' => $this->event,
-            'u' => $user,
-            'driver' => $this->driver
-        ];
-        call_user_func_array($callback, $callback_params);
-
-        // Сохранение пользователя...
-        if ($user->hasChanged()) {
-            // Бот изменил пользователя, сохраняем его данные
-            UserModel::updateObject($user->getDbObject());
-        }
-
-        // Завершение работы драйвера...
-
-        exit();
+        $this->processRequest($callback, []);
     }
 
     // Подключает обработчик события без условия
@@ -91,13 +103,83 @@ class Bot {
         if (!is_a($this->event, $event_classname, true)) {
             return;
         }
+        $this->processRequest($callback, []);
+    }
 
-        // Вызов обработчика
-        $callback_params = [
-            'e' => $this->event,
-            'u' => $this->event->getUser(),
-            'driver' => $this->driver
-        ];
-        call_user_func_array($callback, $callback_params);
+    // Подключает обработчик команды
+    // Команда должна быть только текстовым сообщением
+    public function onCommand(string $template, callable $callback) {
+        if (!is_a($this->event, PlainMessageEvent::class, true)) {
+            // Не текстовое сообщение
+            return;
+        }
+
+        // Определяем что будет параметрами
+		$pattern = '/^'.preg_replace(
+			['/\//','/{(\w+)}/'],
+			['\\\/', '(?<$1>.*)'],
+			$template
+		).'$/';
+
+
+        if (!preg_match($pattern, $this->event->getText(), $named_groups)) {
+            // Обрабатываемое событие - не для этого обработчика
+            return;
+		};
+
+        // Оставляем только строковые ключи, т.к. в processRequest
+        // $named_groups будут соединены со стандартными параметрами, которые
+        // передаются только по названиям, не по позициям. Если в $named_groups
+        // попадётся числовой ключ, позиционный аргумент передастся после
+        // ключевого, что приведёт к ошибке
+        $named_groups_filter = array_filter(
+            $named_groups,
+            function ($k) {
+                return is_string($k);
+            },
+            ARRAY_FILTER_USE_KEY
+        );
+
+        // Вызов обработки с пойманными параметрами
+        $this->processRequest($callback, $named_groups_filter);
     }
+
+    // Подключает обработчик обратного вызова
+    //~ public function onCallback(CallbackType $cbType, callable $responseCallback) {
+        //~ if (!is_a($this->event, ::class, true)) {
+            //~ // Не событие обратного вызова
+            //~ return;
+        //~ }
+
+        //~ // Определяем что будет параметрами
+		//~ $pattern = '/^'.preg_replace(
+			//~ ['/\//','/{(\w+)}/'],
+			//~ ['\\\/', '(?<$1>.*)'],
+			//~ $template
+		//~ ).'$/';
+
+
+        //~ if (!preg_match($pattern, $this->event->getText(), $named_groups)) {
+            //~ // Обрабатываемое событие - не для этого обработчика
+            //~ return;
+		//~ };
+
+        //~ // Оставляем только строковые ключи, т.к. в processRequest
+        //~ // $named_groups будут соединены со стандартными параметрами, которые
+        //~ // передаются только по названиям, не по позициям. Если в $named_groups
+        //~ // попадётся числовой ключ, позиционный аргумент передастся после
+        //~ // ключевого, что приведёт к ошибке
+        //~ $named_groups_filter = array_filter(
+            //~ $named_groups,
+            //~ function ($k) {
+                //~ return is_string($k);
+            //~ },
+            //~ ARRAY_FILTER_USE_KEY
+        //~ );
+
+        //~ // Вызов обработки с пойманными параметрами
+        //~ $this->processRequest($callback, $named_groups_filter);
+    //~ }
+
+    
 }

+ 47 - 4
src/Common/Commands.php

@@ -6,11 +6,54 @@ namespace BotKit\Common;
 
 use BotKit\Common\Message;
 use BotKit\Enums\Platform;
+use BotKit\Enums\State;
+use BotKit\Keyboards\TermsOfServiceKeyboard;
+use BotKit\Attachments\ImageAttachment;
 
 class Commands {
-    public static function echoMessage($e, $u, $driver) {
-        $driver->reply($e, Message::create($e->getText()));
-        $driver->sendMessage($u, Message::create("DIRECT MESSAGE"));
+
+    // Отправка условий использования
+    public static function showTermsOfService($e, $u, $driver) {
+        $driver->reply(
+            $e,
+            Message::create("[Условия использования]")
+        );
+    }
+
+    public static function helloWorld($e, $u, $driver) {
+        // Проверить: сообщение из чата или от пользователя
+        $url = 'https://www.wallpaperflare.com/static/929/413/133/artwork-aenami-abstract-painting-wallpaper.jpg';
+        $driver->reply(
+            $e,
+            Message::create("Привет, я - Техбот. Моя задача - облегчить твою жизнь, но, для начала, мне нужно задать несколько вопросов"),
+            false
+        );
+        $driver->reply(
+            $e,
+            Message
+                ::create("Ознакомься с условиями использования прежде чем использовать мои функции")
+                ->withKeyboard(new TermsOfServiceKeyboard()),
+            false
+        );
+        $u->setState(State::Registering);
+    }
+
+    // Помощь
+    public static function help($e, $u, $driver) {
+        $driver->reply($e, Message::create("Помощь по BotKit"));
+    }
+
+    // Помощь по теме
+    public static function helpTopic($e, $u, $driver, $topic) {
+        $driver->reply($e, Message::create("Помощь по ".$topic));
+    }
+    
+
+    
+
+    public static function registered($e, $u, $driver) {
+        // Проверить: сообщение из чата или от пользователя
+        $driver->reply($e, Message::create("REGISTERED"));
     }
 
     // Функция приветствия пользователя
@@ -37,4 +80,4 @@ class Commands {
         }
         return $username;
     }
-}
+}

+ 9 - 0
src/Common/Message.php

@@ -22,6 +22,9 @@ class Message {
 	// Имеет ли сообщение изображения
 	private bool $has_images = false;
 
+	// Имеет ли сообщение клавиатуру
+	private bool $has_keyboard = false;
+
 	public function __construct($text) {
 		$this->text = $text;
 	}
@@ -46,6 +49,7 @@ class Message {
 
 	// Добавляет клавиатуру к сообщению
 	public function withKeyboard($keyboard) {
+		$this->has_keyboard = true;
 		$this->keyboard = $keyboard;
 		return $this;
 	}
@@ -67,4 +71,9 @@ class Message {
 		return $this->has_images;
 	}
 	
+	// Возвращает true если сообщение содержит клавиатуру
+	public function hasKeyboard() : bool {
+		return $this->has_keyboard;
+	}
+	
 }

+ 19 - 7
src/Common/User.php

@@ -37,33 +37,45 @@ class User implements \ArrayAccess {
     }
 
     public function getDbObject() : array {
-        return $this->db_object;
+        return $this->dbobj;
+    }
+
+    public function getState() : State {
+        return $this->state;
     }
 
     public function hasChanged() : bool {
         return $this->is_changed;
     }
 
+    // Устанавливает состояние
+    // состояние будет отображаться в $this->state прежде чем пользователь
+    // не будет сохранён
+    public function setState(State $state) : void {
+        $this->state = $state;
+        $this['state'] = $state->value;
+    }
+
     #region Реализация ArrayAccess
     public function offsetSet($offset, $value): void {
         $this->is_changed = true;
         if (is_null($offset)) {
-            $this->db_object[] = $value;
+            $this->dbobj[] = $value;
         } else {
-            $this->db_object[$offset] = $value;
+            $this->dbobj[$offset] = $value;
         }
     }
 
     public function offsetExists($offset): bool {
-        return isset($this->db_object[$offset]);
+        return isset($this->dbobj[$offset]);
     }
 
     public function offsetUnset($offset): void {
-        unset($this->db_object[$offset]);
+        unset($this->dbobj[$offset]);
     }
 
-    public function offsetGet($offset) {
-        return isset($this->db_object[$offset]) ? $this->db_object[$offset] : null;
+    public function offsetGet($offset) : mixed {
+        return isset($this->dbobj[$offset]) ? $this->dbobj[$offset] : null;
     }
     #endregion
 }

+ 12 - 0
src/Drivers/Driver.php

@@ -32,4 +32,16 @@ interface Driver {
 
     // Возвращает ник пользователя, например @aquadim
     public function getUserNick(User $u) : string;
+
+    // Событие перед началом обработки запроса
+    public function onProcessStart() : void;
+
+    // Событие после ensureDriversLoaded
+    public function onSelected() : void;
+
+    // Событие завершения обработки
+    public function onProcessEnd() : void;
+
+    // Событие сохранения пользователя
+    public function onUserSave(User $user) : void;
 }

+ 290 - 0
src/Drivers/TestDriver.php

@@ -0,0 +1,290 @@
+<?php
+namespace BotKit\Drivers;
+
+// Драйвер для тестов
+
+use BotKit\Events\Event;
+use BotKit\Events\PlainMessageEvent;
+use BotKit\Events\UnknownEvent;
+use BotKit\Events\MemberJoinedEvent;
+use BotKit\Events\MemberLeftEvent;
+use BotKit\Common\User;
+use BotKit\Common\Chat;
+use BotKit\Common\Message;
+use BotKit\Common\Database;
+use BotKit\EventAttachments\DocumentAttachment;
+use BotKit\EventAttachments\ImageAttachment;
+use BotKit\Enums\Platform;
+use BotKit\Enums\State;
+use BotKit\Enums\KeyboardButtonColor;
+use BotKit\Models\UserModel;
+use BotKit\KeyboardButtons\CallbackButton;
+
+class TestDriver implements Driver {
+    // Платформа бота
+    private Platform $platform;
+
+    // Буфер действий
+    private array $actions;
+
+    public function __construct() {
+        $this->platform = Platform::Test;
+    }
+
+    #region Driver
+
+    public function forThis() : bool {
+        return true;
+    }
+
+    public function onSelected() : void {
+        set_error_handler([$this, "errorHandler"], E_ALL);
+        set_exception_handler([$this, "exceptionHandler"]);
+    }
+
+    public function onProcessStart() : void {
+    }
+
+    public function onProcessEnd() : void {
+        $this->echoActions();
+    }
+
+    public function getEvent() : Event {
+        $data = json_decode(file_get_contents("php://input"));
+        $db = Database::getConnection();
+
+        $details = $data->details;
+        $chat = new Chat(42);
+
+        if ($data->type == 'callback') {
+            // Обратный вызов
+
+            $user = $this->getUser();
+        } else if ($data->type == 'botKitMsg') {
+            // Обычное текстовое сообщение
+
+            // Получение объекта из БД
+            $obj = UserModel::where([
+                ['platform_id', '=', $details->userID],
+                ['platform', '=', $this->platform->value]
+            ]);
+
+            if ($obj === false) {
+                // Пользователя нет в БД
+                $obj = UserModel::create(
+                    $details->userID,
+                    $this->platform->value,
+                    State::HelloWorld->value
+                );
+            }
+
+            $state_obj = State::from($obj['state']);
+
+            $user = new User(
+                $details->userID,
+                $state_obj,
+                "Test username",
+                $obj
+            );
+            $text = $details->text;
+
+            return new PlainMessageEvent(
+                $details->id,
+                $user,
+                $chat,
+                $text,
+                []
+            );
+        }
+
+        return new UnknownEvent();
+    }
+
+    public function reply(PlainMessageEvent $e, Message $msg, bool $empathise = true) {
+        if ($empathise) {
+            $reply_to_id = $e->getMessageID();
+        } else {
+            $reply_to_id = -1;
+        }
+        $this->sendInternal($msg, $reply_to_id);
+    }
+
+    public function sendMessage(User $u, Message $msg) {
+        $this->sendInternal($msg, -1);
+    }
+
+    public function sendToChat(Chat $chat, Message $msg) {
+        $this->sendInternal($msg, -1);
+    }
+
+    public function getUserNick(User $u) : string {
+        return $u->getNick();
+    }
+
+    public function onUserSave(User $user) : void {
+        $this->actions[] = [
+            "action" => "info",
+            "title" => "Состояние пользователя изменено",
+            "body" => "Новое состояние: ".serialize($user->getState())
+        ];
+    }
+    
+    #endregion
+    
+    // Выводит все события в JSON
+    private function echoActions() {
+        echo json_encode($this->actions);
+        exit();
+    }
+
+    public function errorHandler(
+        int $errno,
+        string $errstr,
+        string $errfile = null,
+        int $errline = null,
+        array $errcontext = null
+    ): bool {
+
+        $meaning = [
+            E_ERROR => "error",
+            E_WARNING => "warning",
+            E_PARSE => "error",
+            E_NOTICE => "warning",
+            E_CORE_ERROR => "error",
+            E_CORE_WARNING => "warning",
+            E_COMPILE_ERROR => "error",
+            E_COMPILE_WARNING => "warning",
+            E_USER_ERROR => "error",
+            E_USER_WARNING => "warning",
+            E_USER_NOTICE => "warning",
+            E_STRICT => "warning",
+            E_RECOVERABLE_ERROR => "error",
+            E_DEPRECATED => "warning",
+            E_USER_DEPRECATED => "warning"
+        ];
+
+        $this->actions[] = [
+            "action" => $meaning[$errno]."Message",
+            "error" => [
+                "line" => $errline,
+                "file" => $errfile,
+                "trace" => "<Нет стека вызовов>",
+                "msg" => $errstr
+            ]
+        ];
+
+        if ($meaning[$errno] === 'error') {
+            // Если произошла фатальная ошибка, завершаем работу
+            $this->echoActions();
+            return true; // Не достигается
+        } else {
+            return false;
+        }
+    }
+
+    public function exceptionHandler($ex) : void {
+        $this->actions[] = [
+            "action" => "errorMessage",
+            "error" => [
+                "line" => $ex->getLine(),
+                "file" => $ex->getFile(),
+                "trace" => $ex->getTraceAsString(),
+                "msg" => $ex->getMessage()
+            ]
+        ];
+        $this->echoActions();
+    }
+
+    // Отправляет сообщение
+    private function sendInternal(Message $msg, int $reply_to_id) : void {
+        $attachments = [];
+
+        // Поиск клавиатур
+        if ($msg->hasKeyboard()) {
+            $keyboard = $msg->getKeyboard();
+
+            // Определение типа
+            if ($keyboard->inline) {
+                $attachment_type = "inlineKeyboard";
+            } else {
+                $attachment_type = "keyboard";
+            }
+
+            $serialized_layout = [];
+
+            // Разметка
+            $layout = $keyboard->getLayout();
+            foreach ($layout as $row) {
+                $serialized_row = [];
+                foreach ($row as $button) {
+
+                    // Определение типа
+                    if (is_a($button, CallbackButton::class)) {
+                        // Кнопка обратного вызова
+                        $button_type = "callbackButton";
+                    } else {
+                        $button_type = "button";
+                    }
+
+                    // Определение цвета
+                    switch ($button->getColor()) {
+                        case KeyboardButtonColor::Primary:
+                            $button_color = "primary";
+                            break;
+                        case KeyboardButtonColor::Secondary:
+                            $button_color = "secondary";
+                            break;
+                        default:
+                            $button_color = "primary";
+                            break;
+                    }
+                    
+                    $serialized_row[] = [
+                        "type" => $button_type,
+                        "color" => $button_color,
+                        "label" => $button->getText(),
+                        "payload" => $button->getPayload()
+                    ];
+                }
+                $serialized_layout[] = $serialized_row;
+            }
+
+            $attachments[] = [
+                "type" => $attachment_type,
+                "layout" => $serialized_layout
+            ];
+        }
+
+        // Поиск изображений
+        if ($msg->hasImages()) {
+            $images = $msg->getImages();
+            foreach ($images as $image) {
+                $attachments[] = [
+                    'type' => 'image',
+                    'url' => $image->getValue()
+                ];
+            }
+        }
+
+        $this->actions[] = [
+            "action" => "newMessage",
+            "message" => [
+                "text" => $msg->getText(),
+                "reply_to" => $reply_to_id,
+                "attachments" => $attachments
+            ]
+        ];
+    }
+
+    // Информация о переменной
+    public function varDump(string $title, $variable) {
+        ob_start();
+        var_dump($variable);
+        $info = ob_get_clean();
+        $this->actions[] = [
+            "action" => "varDump",
+            "title" => $title,
+            "info" => $info 
+        ];
+    }
+}

+ 1 - 0
src/Drivers/TgBotDriver.php

@@ -11,6 +11,7 @@ use BotKit\Events\MemberLeftEvent;
 use BotKit\Common\User;
 use BotKit\Common\Chat;
 use BotKit\Common\Message;
+use BotKit\Common\Database;
 use BotKit\EventAttachments\DocumentAttachment;
 use BotKit\EventAttachments\ImageAttachment;
 use BotKit\Enums\Platform;

+ 0 - 11
src/Enums/EventType.php

@@ -1,11 +0,0 @@
-<?php
-// Перечисление для всех типов запросов
-
-namespace BotKit\Enums;
-
-enum EventType: int {
-	case Fallback			= 0;	// Команда не обработана
-	case Other				= 1;	// Тип события не поддерживается
-	case PlainMessage		= 2;	// Текстовое сообщение
-	case CallbackMessage	= 3;	// Сообщение обратного вызова
-}

+ 2 - 1
src/Enums/Platform.php

@@ -7,5 +7,6 @@ namespace BotKit\Enums;
 
 enum Platform: int {
     case TelegramOrg = 0;
-    case VkCom = 0;
+    case VkCom = 1;
+    case Test = 2;
 }

+ 44 - 0
src/Events/CallbackEvent.php

@@ -0,0 +1,44 @@
+<?php
+namespace BotKit\Events;
+
+use BotKit\Common\User;
+use BotKit\Common\Chat;
+
+class CallbackEvent extends Event {
+    public function __construct(
+        // ID сообщения с которым связан обратный вызов
+        private $message_id,
+
+        // Пользователь, вызвавший обратный вызов
+        protected User $user,
+
+        // Чат, в котором был вызван обратный вызов
+        private Chat $chat,
+
+        // Тип обратного вызова
+        private CallbackType $callbackType,
+
+        // Параметры обратного вызова
+        // Ключи массива - названия параметров
+        // Значения - значения
+        private array $params,
+    ) {}
+
+    public function getMessageID() {
+        return $this->message_id;
+    }
+
+    public function getChat() : Chat {
+        return $this->chat;
+    }
+
+    public function getCallbackType() : string {
+        return $this->callbackType;
+    }
+
+    public function getParams() : array {
+        return $this->params;
+    }
+
+    
+}

+ 2 - 2
src/Events/EmptyEvent.php → src/Events/UnknownEvent.php

@@ -1,10 +1,10 @@
 <?php
 namespace BotKit\Events;
 
-// Необработанное событие
+// Неизвестное событие
 
 use BotKit\Common\User;
 use BotKit\Common\Chat;
 
-class EmptyEvent extends Event {
+class UnknownEvent extends Event {
 }

+ 19 - 3
src/KeyboardButtons/CallbackKeyboardButton.php → src/KeyboardButtons/CallbackButton.php

@@ -5,19 +5,35 @@
 namespace BotKit\KeyboardButtons;
 
 use BotKit\Enums\KeyboardButtonColor;
+use BotKit\Enums\CallbackType;
+
+class CallbackButton extends KeyboardButton {
 
-class CallbackKeyboardButton extends KeyboardButton {
 	// Тип вызова. Аналог состояния пользователя
 	private $type;
 
-	public function __construct(string $text, $type, $color=KeyboardButtonColor::None, $payload=[]) {
+	// Дополнительная информация
+	private $payload;
+
+	public function __construct
+	(
+		string $text,
+		CallbackType $type,
+		$color = KeyboardButtonColor::None,
+		array $payload = []
+	)
+	{
 		$this->text = $text;
 		$this->color = $color;
-		$this->payload = $payload;
 		$this->type = $type;
+		$this->payload = $payload;
 	}
 
 	public function getType() {
 		return $this->type;
 	}
+
+	public function getPayload() {
+		return $this->payload;
+	}
 }

+ 2 - 5
src/KeyboardButtons/KeyboardButton.php

@@ -13,14 +13,11 @@ abstract class KeyboardButton {
 	// Цвет кнопки
 	protected $color;
 
-	// Дополнительная драйвер-специфичная информация
-	protected array $payload;
-
 	public function getText() {
 		return $this->text;
 	}
 
-	public function getPayload() {
-		return $this->payload;
+	public function getColor() {
+		return $this->color;
 	}
 }

+ 0 - 18
src/Keyboards/InlineKeyboard.php

@@ -1,18 +0,0 @@
-<?php
-// Приложение к сообщению: клавиатура (в самом сообщении)
-// Родительский класс - только для наследования
-
-namespace BotKit\Keyboards;
-
-abstract class InlineKeyboard {
-	// Можно ли кэшировать эту клавиатуру
-	public static bool $cacheable = false;
-
-	// Расположение кнопок в клавиатуре
-	private array $layout;
-
-	// Возвращает расположение кнопок
-	public function getLayout() : array {
-		return $this->layout;
-	}
-}

+ 1 - 1
src/Keyboards/Keyboard.php

@@ -9,7 +9,7 @@ abstract class Keyboard {
 	public static bool $cacheable = false;
 
 	// Расположение кнопок в клавиатуре
-	private array $layout;
+	protected array $layout;
 
 	// Возвращает расположение кнопок
 	public function getLayout() : array {

+ 4 - 4
src/Keyboards/SelectUserTypeKeyboard.php

@@ -3,24 +3,24 @@
 
 namespace BotKit\Keyboards;
 
-use BotKit\KeyboardButtons\CallbackKeyboardButton;
+use BotKit\KeyboardButtons\CallbackButton;
 use BotKit\Enums\KeyboardButtonColor;
 use BotKit\Enums\CallbackType;
 
-class SelectUserTypeKeyboard extends InlineKeyboard {
+class SelectUserTypeKeyboard extends Keyboard {
 
 	public function __construct() {
 		
 		$this->layout =
 		[
 			[
-				new CallbackKeyboardButton(
+				new CallbackButton(
 					"Я студент",
 					CallbackType::SelectedAccountType,
 					KeyboardButtonColor::Primary,
 					["account"=> "student"]
 				),
-				new CallbackKeyboardButton(
+				new CallbackButton(
 					"Я преподаватель",
 					CallbackType::SelectedAccountType,
 					KeyboardButtonColor::Primary,

+ 4 - 3
src/Keyboards/TosKeyboard.php → src/Keyboards/TermsOfServiceKeyboard.php

@@ -3,15 +3,16 @@
 
 namespace BotKit\Keyboards;
 
-use BotKit\KeyboardButtons\CallbackKeyboardButton;
+use BotKit\KeyboardButtons\CallbackButton;
 use BotKit\Enums\KeyboardButtonColor;
 use BotKit\Enums\CallbackType;
 
-class TosKeyboard extends InlineKeyboard {
+class TermsOfServiceKeyboard extends Keyboard {
 	#region Драйвер-зависимые свойства
 	public $tg_resize=true;
 	public $tg_onetime=false;
 	public $tg_specific=true;
+	public $inline=true;
 	#endregion
 
 	public function __construct() {
@@ -19,7 +20,7 @@ class TosKeyboard extends InlineKeyboard {
 		$this->layout =
 		[
 			[
-				new CallbackKeyboardButton(
+				new CallbackButton(
 					"Показать условия использования",
 					CallbackType::TOS,
 					KeyboardButtonColor::Primary