├── autoload.php ├── .gitignore ├── composer.json ├── LICENSE ├── src ├── Docs.php ├── ErrorConstants.php ├── KeyboardBuilder.php ├── Logger.php ├── Messages.php ├── Bot.php └── Config.php └── README.md /autoload.php: -------------------------------------------------------------------------------- 1 | =7.0", 16 | "ext-curl": "*", 17 | "ext-json": "*" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "FunnyRain\\vkbot\\": "src/" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 FunnyRain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Docs.php: -------------------------------------------------------------------------------- 1 | bot = $bot; 17 | } 18 | 19 | /** 20 | * @param string $type 21 | * @param $peerId 22 | * @return string 23 | */ 24 | public function getUploadServer(string $type, $peerId): string { 25 | return $this->bot->api("docs.getMessagesUploadServer", [ 26 | "peer_id" => $peerId, 27 | "type" => $type 28 | ])["upload_url"]; 29 | } 30 | 31 | /** 32 | * @param string $path 33 | * @param string $type 34 | * @param $peerId 35 | * @return array|false 36 | */ 37 | public function getUrlDoc(string $path, string $type, $peerId) { 38 | if (!class_exists('CURLFile', false)) return false; 39 | if (!file_exists($path)) return false; 40 | 41 | $url = $this->getUploadServer($type, $peerId); 42 | 43 | $myCurl = curl_init(); 44 | curl_setopt_array($myCurl, [ 45 | CURLOPT_URL => $url, 46 | CURLOPT_RETURNTRANSFER => true, 47 | CURLOPT_HTTPHEADER => [ 48 | 'Content-Type: multipart/form-data', 49 | ], 50 | CURLOPT_POST => true, 51 | CURLOPT_POSTFIELDS => [ 52 | "file" => new \CURLFile($path), 53 | ] 54 | ]); 55 | $response = json_decode(curl_exec($myCurl), 1); 56 | 57 | return $this->bot->api("docs.save", [ 58 | "file" => $response["file"] 59 | ]); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VKBOT / Простая библиотека для создания бота 2 | >Прошлую версию библиотеки с полной документацией можно скачать тут [Releases](https://github.com/FunnyRain/vkbot/releases) :grin: 3 | 4 | ## Документация 5 | * [https://funnyrain.gitbook.io/vkbot/](https://funnyrain.gitbook.io/vkbot/) 6 | ## Что есть? 7 | - Bots LongPoll API 8 | - Обработка команд 9 | - Обработка событий 10 | - Работа с кнопками 11 | - Загрузка документов 12 | 13 | ## Что планируется? 14 | 15 | - Рассылка сообщений 16 | - Создание виджета 17 | 18 | ## Примеры использования 19 | ###### Добавление клавиатуры / Вызов по команде "кнопки": 20 | ```php 21 | setToken('токен'); 25 | 26 | $bot->start(function($data)use($bot){ 27 | 28 | $msg = $bot->getMessage(); 29 | $kb = $bot->kBuilder(); // Подключаем билдера кнопок 30 | if ($msg->get() == "кнопки") { 31 | $kb->create( 32 | [ 33 | [ // <-- Начало первой строки 34 | $kb->button('красная кнопка', 'red'), 35 | $kb->button('зеленая кнопка', 'green'), 36 | $kb->button('синяя кнопка', 'blue') 37 | ], // <-- Конец первой строки 38 | [ // <-- Начало второй строки 39 | $kb->link('кнопка с ссылкой', 'http://example.com'), 40 | $kb->location() 41 | ] // <-- Конец второй строки 42 | ] 43 | // one_time (По стандарту false), 44 | // inline (По стандарту false) 45 | ); 46 | /** 47 | * Должно вывести клавиатуру в таком виде: 48 | * [--] [--] [--] 49 | * [--] [--] 50 | */ 51 | $msg->reply('Отправляю клавиатуру:', [ 52 | 'keyboard' => $kb->get() 53 | ]); 54 | } 55 | 56 | }); 57 | ``` 58 | ###### Простой пример обработки события "Приглашение бота в беседу": 59 | ```php 60 | setToken('токен'); 64 | 65 | $bot->start(function($data)use($bot){ 66 | 67 | // chat_invite_user - Событие добавления в беседу 68 | // Список всех событий: https://vk.com/dev/groups_events 69 | $bot->isAction('chat_invite_user', function($data)use($bot) { 70 | $msg = $bot->getMessage(); 71 | if ($data['member_id'] == -$bot->group_id) 72 | $msg->reply('спасибо за приглашение'); 73 | }); 74 | 75 | }); 76 | ``` 77 | ###### Простой пример отправки сообщения на команду "info": 78 | ```php 79 | setToken('токен'); 83 | 84 | $bot->start(function($data)use($bot){ 85 | 86 | $msg = $bot->getMessage(); 87 | if ($msg->get() == "info") { 88 | $msg->reply( 89 | "привет" 90 | ); 91 | //$msg->sendSticker(51077); 92 | } 93 | 94 | }); 95 | ``` 96 | -------------------------------------------------------------------------------- /src/ErrorConstants.php: -------------------------------------------------------------------------------- 1 | "Произошла неизвестная ошибка.", 5 | 2 => "Приложение выключено.", 6 | 3 => "Передан неизвестный метод.", 7 | 4 => "Неверная подпись.", 8 | 5 => "Авторизация пользователя не удалась.", 9 | 6 => "Слишком много запросов в секунду.", 10 | 7 => "Нет прав для выполнения этого действия.", 11 | 8 => "Неверный запрос.", 12 | 9 => "Слишком много однотипных действий.", 13 | 10 => "Произошла внутренняя ошибка сервера.", 14 | 14 => "Требуется ввод кода с картинки (Captcha).", 15 | 15 => "Доступ запрещён.", 16 | 18 => "Страница удалена или заблокирована.", 17 | 19 => "Контент недоступен.", 18 | 21 => "Данное действие разрешено только для Standalone и Open API приложений.", 19 | 23 => "Метод был выключен.", 20 | 27 => "Ключ доступа сообщества недействителен.", 21 | 28 => "Ключ доступа приложения недействителен.", 22 | 29 => "Достигнут количественный лимит на вызов метода.", 23 | 30 => "Профиль является приватным.", 24 | 33 => "Not implemented yet.", 25 | 100 => "Один из необходимых параметров был не передан или неверно.", 26 | 113 => "Неверный идентификатор пользователя.", 27 | 114 => "Недопустимый идентификатор альбома.", 28 | 118 => "Недопустимый сервер.", 29 | 121 => "Неверный хэш.", 30 | 150 => "Неверный timestamp.", 31 | 200 => "Доступ к альбому запрещён.", 32 | 201 => "Доступ к аудио запрещён.", 33 | 203 => "Доступ к сообществу запрещён.", 34 | 212 => "Access to post comments denied", 35 | 213 => "Нет доступа к комментированию записи", 36 | 214 => "Access to adding post denied", 37 | 219 => "Рекламный пост уже недавно публиковался.", 38 | 220 => "Слишком много получателей.", 39 | 222 => "Запрещено размещение ссылок в комментариях", 40 | 223 => "Превышен лимит комментариев на стене", 41 | 224 => "Too many ads posts", 42 | 225 => "Donut is disabled", 43 | 300 => "Альбом переполнен.", 44 | 900 => "Нельзя отправлять сообщение пользователю из черного списка", 45 | 901 => "Пользователь запретил отправку сообщений от имени сообщества", 46 | 902 => "Нельзя отправлять сообщения этому пользователю в связи с настройками приватности", 47 | 909 => "Невозможно отредактировать сообщение после 24 часов ", 48 | 910 => "Невозможно отредактировать сообщение, поскольку оно слишком большое ", 49 | 911 => "Keyboard format is invalid", 50 | 912 => "This is a chat bot feature, change this status in settings", 51 | 913 => "Слишком много пересланных сообщений", 52 | 914 => "Сообщение слишком длинное", 53 | 917 => "У вас нет доступа в эту беседу", 54 | 920 => "Невозможно отредактировать сообщение такого типа ", 55 | 921 => "Невозможно переслать выбранные сообщения", 56 | 925 => "You are not admin of this chat", 57 | 936 => "Contact not found", 58 | 940 => "Too many posts in messages", 59 | 943 => "Cannot use this intent", 60 | 944 => "Limits overflow for this intent", 61 | 945 => "Chat was disabled", 62 | 946 => "Chat not supported", 63 | 949 => "Can't edit pinned message yet ", 64 | 3300 => "Recaptcha needed.", 65 | 3609 => "Token extensions required.", 66 | ]; 67 | -------------------------------------------------------------------------------- /src/KeyboardBuilder.php: -------------------------------------------------------------------------------- 1 | buttons = []; 19 | foreach ($keyboard as $kfd => $kv) { 20 | $this->buttons[] = $kv; 21 | } 22 | $this->keyboard = ['one_time' => $one_time, 'inline' => $inline, 'buttons' => $this->buttons]; 23 | } 24 | 25 | /** 26 | * Вывод клавиатуры 27 | * Вернет клавиатуру для 'keyboard' в сообщении 28 | */ 29 | public function get() { 30 | return json_encode($this->keyboard, JSON_UNESCAPED_UNICODE); 31 | } 32 | 33 | /** 34 | * Добавляет стандартную кнопку 35 | * @param string $text Текст кнопки 36 | * @param string $color Цвет кнопки 37 | * @param string $payload Дополнительная информация 38 | * @return array Вернет кнопку 39 | */ 40 | public function button(string $text, string $color = 'default', string $payload = ''): array { 41 | return [ 42 | 'action' => [ 43 | 'type' => 'text', 44 | 'payload' => json_encode($payload, JSON_UNESCAPED_UNICODE), 45 | 'label' => $text 46 | ], 47 | 'color' => self::replaceColor($color) 48 | ]; 49 | } 50 | 51 | /** 52 | * Кнопка с ссылкой 53 | * @param string $text Текст кнопки 54 | * @param string $link Ссылка 55 | * @return array Вырнет кнопку с ссылкой 56 | */ 57 | public function link(string $text, string $link): array { 58 | return [ 59 | 'action' => [ 60 | 'type' => 'open_link', 61 | 'link' => $link, 62 | 'label' => $text 63 | ] 64 | ]; 65 | } 66 | 67 | /** 68 | * Кнопка с запросом геолокации 69 | * @return array Вернет кнопку с геолокацией 70 | */ 71 | public function location(): array { 72 | return [ 73 | 'action' => [ 74 | 'type' => 'location' 75 | ] 76 | ]; 77 | } 78 | 79 | /** 80 | * Кнопка оплаты 81 | * @link https://vk.com/dev/bots_docs_3 82 | * 83 | * @param string $hash параметры платежа VK Pay и идентификатор приложения 84 | * @return array 85 | */ 86 | public function vkpay(string $hash): array { 87 | return [ 88 | 'action' => [ 89 | 'type' => 'vkpay', 90 | 'hash' => $hash 91 | ] 92 | ]; 93 | } 94 | 95 | /** 96 | * Кнопка открытия приложения 97 | * @param string $text Текст кнопки 98 | * @param integer $app_id Айди вызываемого приложения с типом VK Apps. 99 | * @param integer $owner_id Айди сообщества, в котором установлено приложение, если требуется открыть в контексте сообщества 100 | * @param string $hash Хэш для навигации в приложении, будет передан в строке параметров запуска после символа # 101 | * @return array Вернет кнопку открытия приложения 102 | */ 103 | public function vkapps(string $text, int $app_id, int $owner_id, string $hash = ''): array { 104 | return [ 105 | 'action' => [ 106 | 'type' => 'open_app', 107 | 'app_id' => $app_id, 108 | 'owner_id' => $owner_id, 109 | 'label' => $text, 110 | 'hash' => (empty($hash)) ? null : $hash 111 | ] 112 | ]; 113 | } 114 | 115 | /** 116 | * Кнопка Callback 117 | * @param string $text Текст кнопки 118 | * @param string $payload Дополнительная информация 119 | * @return array Вернет callback кнопку 120 | */ 121 | public function callback(string $text, string $payload = ''): array { 122 | return [ 123 | 'action' => [ 124 | 'type' => 'callback', 125 | 'label' => $text, 126 | 'payload' => json_encode($payload, JSON_UNESCAPED_UNICODE) 127 | ] 128 | ]; 129 | } 130 | 131 | /** 132 | * Убирает клавиатуру из диалога 133 | * @return array 134 | */ 135 | public function remove(): string { 136 | unset($this->keyboard); 137 | unset($this->buttons); 138 | return json_encode(['one_time' => true, 'buttons' => []], JSON_UNESCAPED_UNICODE); 139 | } 140 | 141 | static function replaceColor(string $color): string { 142 | return str_replace(['red', 'green', 'white', 'blue'], ['negative', 'positive', 'default', 'primary'], $color); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Logger.php: -------------------------------------------------------------------------------- 1 | "0;30", 12 | "dark_gray" => "1;30", 13 | "light_gray" => "0;37", 14 | "blue" => "0;34", 15 | "light_blue" => "1;34", 16 | "green" => "0;32", 17 | "light_green" => "1;32", 18 | "cyan" => "0;36", 19 | "light_cyan" => "1;36", 20 | "red" => "0;31", 21 | "light_red" => "1;31", 22 | "purple" => "0;35", 23 | "light_purple" => "1;35", 24 | "brown" => "0;33", 25 | "yellow" => "1;33", 26 | "white" => "1;37" 27 | ]; 28 | /** --------- */ 29 | public $background_colors = [ 30 | "black" => "40", 31 | "red" => "41", 32 | "green" => "42", 33 | "yellow" => "43", 34 | "blue" => "44", 35 | "magenta" => "45", 36 | "cyan" => "46", 37 | "light_gray" => "47", 38 | ]; 39 | 40 | public function __construct() { 41 | if (!file_exists($this->default_path)) { 42 | if (mkdir($this->default_path, 0777)) { 43 | $this->isWrite = true; 44 | } else $this->error('Не удалось создать папку с логами', 'Все логи будут выводиться в консоль!'); 45 | } else $this->isWrite = true; 46 | } 47 | 48 | /** 49 | * Записывает в файл с логами 50 | * @param string $log 51 | * @return void 52 | */ 53 | public function writeLog(string $log = ''): void { 54 | file_put_contents($this->default_path . date("d.m.y") . '.txt', $log, FILE_APPEND); 55 | echo $log; 56 | } 57 | 58 | /** 59 | * Вывод в консоль (Для стандартных сообщений) 60 | * @param string $title 61 | * @param string $subtitle 62 | * @return void 63 | */ 64 | public function log(string $title = "", string $subtitle = null): void { 65 | $log = PHP_EOL . $this->color("[Лог] > " . date("d.m.y, H:i:s") . "> ", "blue") . $this->color($title, "white"); 66 | if (isset($subtitle)) 67 | $log .= PHP_EOL . "\t" . $this->color($subtitle, "green"); 68 | if ($this->isWrite) 69 | $this->writeLog($log); 70 | else 71 | echo $log; 72 | } 73 | 74 | /** 75 | * Вывод в консоль (Для сообщений с ошибками) 76 | * @param string $title 77 | * @param string $subtitle 78 | * @return void 79 | */ 80 | public function error(string $title = "", string $subtitle = null): void { 81 | $log = PHP_EOL . $this->color("[Ошибка] > " . date("d.m.y, H:i:s") . "> ", "red") . $this->color($title, "white"); 82 | if (isset($subtitle)) 83 | $log .= PHP_EOL . "\t" . $this->color($subtitle, "green"); 84 | if ($this->isWrite) 85 | $this->writeLog($log); 86 | else 87 | echo $log; 88 | } 89 | 90 | /** 91 | * Вывод в консоль (Для сообщений с предупреждением) 92 | * @param string $title 93 | * @param string $subtitle 94 | * @return void 95 | */ 96 | public function warning(string $title = "", string $subtitle = null): void { 97 | $log = PHP_EOL . $this->color("[Предупреждение] > " . date("d.m.y, H:i:s") . "> ", "yellow") . $this->color($title, "white"); 98 | if (isset($subtitle)) 99 | $log .= PHP_EOL . "\t" . $this->color($subtitle, "green"); 100 | if ($this->isWrite) 101 | $this->writeLog($log); 102 | else 103 | echo $log; 104 | } 105 | 106 | /** 107 | * Вывод в консоль (Для сообщений разработчикам) 108 | * @param string $title 109 | * @param string $subtitle 110 | * @return void 111 | */ 112 | public function debug(string $title = "", string $subtitle = null): void { 113 | echo PHP_EOL . $this->color("[Дебаг] > " . date("d.m.y, H:i:s") . "> ", "purple") . $this->color($title, "white"); 114 | if (isset($subtitle)) 115 | echo PHP_EOL . "\t" . $this->color($subtitle, "light_purple"); 116 | } 117 | 118 | /** 119 | * Пример: color('желтый текст с черным фоном', 'yellow', 'black'); 120 | * @param string $string 121 | * @param string $foreground_color 122 | * @param string $background_color 123 | * @return string 124 | */ 125 | public function color(string $string, string $foreground_color = null, string $background_color = null): string { 126 | $colored_string = ""; 127 | if (isset($this->foreground_colors[$foreground_color])) { 128 | $colored_string .= "\033[" . $this->foreground_colors[$foreground_color] . "m"; 129 | } 130 | if (isset($this->background_colors[$background_color])) { 131 | $colored_string .= "\033[" . $this->background_colors[$background_color] . "m"; 132 | } 133 | $colored_string .= $string . "\033[0m"; 134 | return $colored_string; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Messages.php: -------------------------------------------------------------------------------- 1 | bot = $bot; 9 | } 10 | 11 | /** 12 | * Получение и Сравнение сообщения 13 | * @param array|string $object 14 | * @return string 15 | */ 16 | public function get($object = []): string { 17 | if (!is_array($object)) { 18 | if (substr_count($object, '/') === 2) { 19 | return preg_match($object, (isset($this->bot->vkdata['text'])) ? $this->bot->vkdata['text'] : ""); 20 | } else { 21 | if (isset($this->bot->vkdata['text']) and $this->bot->vkdata['text'] == $object) 22 | return true; 23 | else 24 | return false; 25 | } 26 | } 27 | 28 | if (empty($object)) $object = $this->bot->vkdata; 29 | if (isset($object['text'])) 30 | return $object['text']; 31 | else 32 | return ""; 33 | } 34 | 35 | /** 36 | * Получение Payload 37 | * @return string Вернет payload 38 | */ 39 | public function getPayload($object = []): string { 40 | if (empty($object)) $object = $this->bot->vkdata; 41 | if (isset($object['payload'])) 42 | return $object['payload']; 43 | else 44 | return ""; 45 | } 46 | 47 | /** 48 | * Получение цыферного айди пользователя 49 | * @param array $object 50 | * @return integer 51 | */ 52 | public function getUserId(array $object = []): int { 53 | if (empty($object)) $object = $this->bot->vkdata; 54 | return (isset($object['from_id'])) ? $object['from_id'] : 1; 55 | } 56 | 57 | /** 58 | * Получение цыферного айди беседы 59 | * @param array $object 60 | * @return integer 61 | */ 62 | public function getChatId(array $object = []): int { 63 | if (empty($object)) $object = $this->bot->vkdata; 64 | return (isset($object['peer_id'])) ? $object['peer_id'] : Bot::PEER_ID + 1; 65 | } 66 | 67 | /** 68 | * Быстрый ответ сообщением 69 | * @param string $text Сообщение 70 | * @param array $args Дополнительные параметры 71 | * @return array Вернёт айди сообщения 72 | */ 73 | public function reply(string $text, array $args = []) { 74 | if (!isset($text) and !isset($args['attachment'])) 75 | return $this->bot->getLog()->error('Не указан текст!'); 76 | 77 | $return = $this->bot->api('messages.send', [ 78 | 'random_id' => rand(), 79 | 'peer_ids' => isset($args['peer_ids']) ? $args['peer_ids'] : $this->bot->vkdata['peer_id'], 80 | 'message' => $text, 81 | 'content_source' => json_encode([ 82 | 'type' => 'message', 83 | 'owner_id' => $this->getUserId(), 84 | 'peer_id' => isset($args['peer_id']) ? $args['peer_id'] : $this->bot->vkdata['peer_id'], 85 | 'conversation_message_id' => $this->bot->vkdata['conversation_message_id'] 86 | ], JSON_UNESCAPED_UNICODE) 87 | ] + $args); 88 | 89 | return $return; 90 | } 91 | 92 | /** 93 | * Отправка стикера отдельным сообщением 94 | * @param integer $id Айди стикера 95 | * @param integer $peer_id Айди получателя (Необязательно) 96 | * @return array Вернёт айди сообщения 97 | */ 98 | public function sendSticker(int $id = 0, int $peer_id = 1): array { 99 | return $this->bot->api('messages.send', [ 100 | 'random_id' => rand(), 101 | 'peer_id' => ($peer_id == 1) ? $this->bot->vkdata['peer_id'] : $peer_id, 102 | 'sticker_id' => $id 103 | ]); 104 | } 105 | 106 | /** 107 | * Отправка сообщения 108 | * @param string $text Текст сообщения 109 | * @param string|integer $peer_ids Айди получател(я/ей) (Перечислять через запятую) 110 | * @param array $args Дополнительные параметры 111 | * @return array Вернёт айди сообщения 112 | */ 113 | public function sendMessage(string $text, $peer_ids, array $args = []) { 114 | if (!isset($text) and !isset($args['attachment'])) 115 | return $this->bot->getLog()->error('Не указан текст!'); 116 | if (preg_match('/(fname|lname|afname|alname|fullname|afullname)/ui', $text)) 117 | $text = $this->replaceNameToMessage(isset($args['from_id']) ? $args['from_id'] : $this->bot->vkdata['from_id'], $text); 118 | 119 | $return = $this->bot->api('messages.send', [ 120 | 'random_id' => rand(), 121 | 'peer_ids' => isset($peer_ids) ? $peer_ids : $this->bot->vkdata['peer_id'], 122 | 'message' => $text, 123 | 'content_source' => json_encode([ 124 | 'type' => 'message', 125 | 'owner_id' => $this->getUserId(), 126 | 'peer_id' => isset($args['peer_id']) ? $args['peer_id'] : $this->bot->vkdata['peer_id'], 127 | 'conversation_message_id' => $this->bot->vkdata['conversation_message_id'] 128 | ], JSON_UNESCAPED_UNICODE) 129 | ] + $args); 130 | 131 | return $return; 132 | } 133 | 134 | /** 135 | * Заменяет ключевые слова на Имя\Фамилию пользователя 136 | * @param integer $user_id Айди пользователя 137 | * @param string $text Текст с ключевыми словами 138 | * @return string Готовый текст 139 | */ 140 | public function replaceNameToMessage(int $user_id, string $text): string { 141 | return str_replace( 142 | ["{fname}", "{lname}", "{afname}", "{alname}", "{fullname}", "{afullname}"], 143 | [ 144 | $this->getInfo($user_id)['first_name'], 145 | $this->getInfo($user_id)['last_name'], 146 | "[id{$user_id}|" . $this->getInfo($user_id)['first_name'] . "]", 147 | "[id{$user_id}|" . $this->getInfo($user_id)['last_name'] . "]", 148 | $this->getInfo($user_id)['first_name'] . " " . $this->getInfo($user_id)['last_name'], 149 | "[id{$user_id}|" . $this->getInfo($user_id)['first_name'] . " " . $this->getInfo($user_id)['last_name'] . "]", 150 | ], 151 | $text 152 | ); 153 | } 154 | 155 | public function edit(string $text, int $conversation_message_id, array $args = []) { 156 | if (!isset($text) or !isset($conversation_message_id)) 157 | return $this->bot->getLog()->error('Не хватает параметров!'); 158 | 159 | $return = $this->bot->api('messages.edit', [ 160 | 'peer_id' => isset($args['peer_id']) ? $args['peer_id'] : $this->bot->vkdata['peer_id'], 161 | 'message' => $text, 162 | 'conversation_message_id' => $conversation_message_id 163 | ] + $args); 164 | 165 | return $return; 166 | } 167 | 168 | public function sendEventAnswer(string $event_id, array $event_data = [], array $args = []) { 169 | if (!isset($event_id) or !isset($event_data)) 170 | return $this->bot->getLog()->error('Не хватает параметров!'); 171 | 172 | $return = $this->bot->api('messages.sendMessageEventAnswer', [ 173 | 'event_id' => $event_id, 174 | 'user_id' => isset($args['user_id']) ? $args['user_id'] : $this->bot->vkdata['user_id'], 175 | 'peer_id' => isset($args['peer_id']) ? $args['peer_id'] : $this->bot->vkdata['peer_id'], 176 | 'event_data' => $event_data 177 | ] + $args); 178 | 179 | return $return; 180 | } 181 | 182 | public function getInfo(int $user_id, string $name_case = ""): array { 183 | return $this->bot->api("users.get", ["user_ids" => $user_id, "name_case" => $name_case])[0]; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/Bot.php: -------------------------------------------------------------------------------- 1 | logger = new Logger(); 34 | $this->builder = new KeyboardBuilder(); 35 | if (!empty($args)) { 36 | foreach ($args as $type) { 37 | if (is_string($type)) $this->token = $type; 38 | if (is_float($type)) $this->v = $type; 39 | if (is_integer($type)) $this->group_id = $type; 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Устанавливает токен 46 | * @param string $token 47 | * @return void 48 | */ 49 | public function setToken(string $token): void { 50 | $this->token = $token; 51 | } 52 | 53 | /** 54 | * Устанавливает версию ВкАпи 55 | * @param float $v 56 | * @return void 57 | */ 58 | public function setVersion(float $v): void { 59 | $this->v = $v; 60 | } 61 | 62 | /** 63 | * Устанавливает айди группы 64 | * @param int $group_id 65 | * @return void 66 | */ 67 | public function setGroupId(int $group_id): void { 68 | $this->group_id = $group_id; 69 | } 70 | 71 | /** 72 | * Класс логирования 73 | * @return Logger 74 | */ 75 | public function getLog(): Logger { 76 | return $this->logger; 77 | } 78 | 79 | /** 80 | * Класс сообщения 81 | * @return Messages 82 | */ 83 | public function getMessage(): Messages { 84 | return new Messages($this); 85 | } 86 | 87 | /** 88 | * Класс сборщика клавиатуры 89 | * @return KeyboardBuilder 90 | */ 91 | public function kBuilder(): KeyboardBuilder { 92 | return $this->builder; 93 | } 94 | 95 | /** 96 | * Проверка токена на валид 97 | * @return void 98 | */ 99 | public function isValidateToken(): void { 100 | $test = $this->api('groups.getById'); 101 | print_r($test); 102 | if (isset($test[0]['id']) and ($test[0]['type'] == 'group' or $test[0]['type'] == 'page')) { 103 | $this->group_id = $test[0]['id']; 104 | $this->getLog()->log('Токен рабочий! group_id: ' . $this->group_id); 105 | $this->api('groups.setLongPollSettings', [ 106 | 'group_id' => $this->group_id, 107 | 'enabled' => 1, 108 | 'api_version' => $this->v, 109 | 'message_new' => 1, 110 | ]); 111 | $this->getLog()->log('Настройки Longpoll в группе выставлены автоматически! Ничего менять не нужно'); 112 | } else die($this->getLog()->error('Токен не рабочий!')); 113 | } 114 | 115 | /** 116 | * Старт бота 117 | * @param [type] $listen 118 | * @return void 119 | */ 120 | public function start($listen) { 121 | if (empty($this->token)) die($this->getLog()->error('Не указан токен!')); 122 | $this->isValidateToken(); 123 | $this->getLongPollServer(); 124 | 125 | while ($data = $this->getRequest()) { 126 | if (!isset($data["ts"])) { 127 | var_dump($data); 128 | $this->getLog()->log("TIMESTAMP не получен...\n"); 129 | continue; 130 | } 131 | 132 | //! Тестируется, возможно, не самое лучшее решение. 133 | $updates = $data['updates']; 134 | if (count($updates) == 0) continue; 135 | 136 | foreach ($updates as $key => $update) { 137 | $object = $updates[$key]['object']; 138 | $this->vkdata = (isset($object['message'])) ? $object['message'] + $object['client_info'] + ['type' => $updates[$key]['type']] 139 | : $object + ['type' => $updates[$key]['type']]; 140 | $listen($object); 141 | } 142 | } 143 | } 144 | 145 | public function isAction(string $type = 'message_new', $listen) { 146 | $object = $this->vkdata; 147 | if (isset($object['action'])) { 148 | if ($object['action']['type'] == $type) { 149 | $listen($object['action']); 150 | } 151 | } 152 | } 153 | 154 | public function call(string $url) { 155 | if (function_exists("curl_init")) 156 | $sendRequest = $this->curl_post($url); 157 | else 158 | $sendRequest = file_get_contents($url); 159 | 160 | $sendRequest = json_decode($sendRequest, true); 161 | if (isset($sendRequest["error"])) { 162 | $error = $sendRequest["error"]["error_code"]; 163 | if (isset(ERRORS[$error])) { 164 | $method = isset($sendRequest["error"]["request_params"][0]["value"]) ? $sendRequest["error"]["request_params"][0]["value"] : null; 165 | $this->getLog()->error(ERRORS[$error], "Метод: {$method}"); 166 | } else { 167 | $this->getLog()->error("Произошла неизвестная ошибка."); 168 | } 169 | } 170 | 171 | if (isset($sendRequest["response"])) { 172 | return $sendRequest["response"]; 173 | } 174 | 175 | return $sendRequest; 176 | } 177 | 178 | /** 179 | * Получение сервера ЛонгПулла 180 | */ 181 | public function getLongPollServer() { 182 | $data = $this->api("groups.getLongPollServer", ["group_id" => $this->group_id]); 183 | $this->getLog()->log("Ссылка лонгпулла обновлена\n"); 184 | list($this->key, $this->server, $this->ts) = [$data['key'], $data['server'], $data['ts']]; 185 | } 186 | 187 | /** 188 | * Получение всех событий 189 | * @return array 190 | */ 191 | public function getRequest(): array { 192 | $result = $this->getData(); 193 | if (isset($result["failed"])) { 194 | if ($result["failed"] == 1) { 195 | unset($this->ts); 196 | $this->ts = $result["ts"]; 197 | } else { 198 | $this->getLongPollServer(); 199 | $result = $this->getData(); 200 | } 201 | } 202 | 203 | $this->ts = $result["ts"]; 204 | return $result; 205 | } 206 | 207 | /** 208 | * @return mixed 209 | */ 210 | public function getData() { 211 | $defult_params = ['act' => 'a_check', 'key' => $this->key, 'ts' => $this->ts, 'wait' => 25]; 212 | $data = json_decode($this->curlRequest($this->server . '?' . http_build_query($defult_params)), 1); 213 | return $data; 214 | } 215 | 216 | /** 217 | * Выполнение Апи запросов, возвращает Массив. 218 | * Список методов: https://vk.com/dev/methods 219 | * @param string $method 220 | * @param array $params 221 | * @return array 222 | */ 223 | public function api(string $method = '', array $params = []): array { 224 | $params["v"] = $this->v; 225 | $params["access_token"] = $this->token; 226 | $params = http_build_query($params); 227 | $url = $this->http_build_query($method, $params); 228 | 229 | return (array)$this->call($url); 230 | } 231 | 232 | private function http_build_query(string $method, string $params = ''): string { 233 | return "https://api.vk.com/method/{$method}?{$params}"; 234 | } 235 | 236 | private function curl_post(string $url) { 237 | if (!function_exists("curl_init")) return false; 238 | $param = parse_url($url); 239 | if ($curl = curl_init()) { 240 | curl_setopt($curl, CURLOPT_URL, $param["scheme"] . "://" . $param["host"] . $param["path"]); 241 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 242 | curl_setopt($curl, CURLOPT_POST, true); 243 | curl_setopt($curl, CURLOPT_POSTFIELDS, $param["query"]); 244 | curl_setopt($curl, CURLOPT_TIMEOUT, 20); 245 | curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); 246 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 247 | $out = curl_exec($curl); 248 | curl_close($curl); 249 | 250 | return $out; 251 | } 252 | 253 | return false; 254 | } 255 | 256 | private function curlRequest($url) { 257 | $myCurl = curl_init(); 258 | curl_setopt_array($myCurl, [ 259 | CURLOPT_URL => $url, 260 | CURLOPT_RETURNTRANSFER => true, 261 | CURLOPT_SSL_VERIFYHOST => false, 262 | CURLOPT_SSL_VERIFYPEER => false 263 | ]); 264 | $response = curl_exec($myCurl); 265 | 266 | return $response; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | Config::PROPERTIES, 31 | "cnf" => Config::CNF, 32 | "conf" => Config::CNF, 33 | "config" => Config::CNF, 34 | "json" => Config::JSON, 35 | "js" => Config::JSON, 36 | "yml" => Config::YAML, 37 | "yaml" => Config::YAML, 38 | "sl" => Config::SERIALIZED, 39 | "serialize" => Config::SERIALIZED, 40 | "txt" => Config::ENUM, 41 | "list" => Config::ENUM, 42 | "enum" => Config::ENUM, 43 | ]; 44 | 45 | /** 46 | * Config constructor. 47 | * @param $file 48 | * @param int $type 49 | * @param array $default 50 | * @param null $correct 51 | */ 52 | public function __construct($file, $type = Config::DETECT, $default = [], &$correct = null) { 53 | $this->load($file, $type, $default); 54 | $correct = $this->correct; 55 | } 56 | 57 | /** 58 | * @param $str 59 | * @return string|string[]|null 60 | */ 61 | public static function fixYAMLIndexes($str) { 62 | return preg_replace("#^([ ]*)([a-zA-Z_]{1}[^\:]*)\:#m", "$1\"$2\":", $str); 63 | } 64 | 65 | /** 66 | * @param $file 67 | * @param int $type 68 | * @param array $default 69 | * @return bool 70 | */ 71 | public function load($file, $type = Config::DETECT, $default = []) { 72 | $this->correct = true; 73 | $this->type = (int)$type; 74 | $this->file = $file; 75 | if (!is_array($default)) { 76 | $default = []; 77 | } 78 | if (!file_exists($file)) { 79 | $this->config = $default; 80 | $this->save(); 81 | } else { 82 | if ($this->type === Config::DETECT) { 83 | $extension = explode(".", basename($this->file)); 84 | $extension = strtolower(trim(array_pop($extension))); 85 | if (isset(Config::$formats[$extension])) { 86 | $this->type = Config::$formats[$extension]; 87 | } else { 88 | $this->correct = false; 89 | } 90 | } 91 | if ($this->correct === true) { 92 | $content = @file_get_contents($this->file); 93 | switch ($this->type) { 94 | case Config::PROPERTIES: 95 | case Config::CNF: 96 | $this->parseProperties($content); 97 | break; 98 | case Config::JSON: 99 | $this->config = json_decode($content, true); 100 | break; 101 | case Config::YAML: 102 | $content = self::fixYAMLIndexes($content); 103 | $this->config = yaml_parse($content); 104 | break; 105 | case Config::SERIALIZED: 106 | $this->config = unserialize($content); 107 | break; 108 | case Config::ENUM: 109 | $this->parseList($content); 110 | break; 111 | default: 112 | $this->correct = false; 113 | return false; 114 | } 115 | if (!is_array($this->config)) { 116 | $this->config = $default; 117 | } 118 | if ($this->fillDefaults($default, $this->config) > 0) { 119 | $this->save(); 120 | } 121 | } else { 122 | return false; 123 | } 124 | } 125 | return true; 126 | } 127 | 128 | /** 129 | * @return bool 130 | */ 131 | public function save() { 132 | if ($this->correct === true) { 133 | $content = null; 134 | switch ($this->type) { 135 | case Config::PROPERTIES: 136 | case Config::CNF: 137 | $content = $this->writeProperties(); 138 | break; 139 | case Config::JSON: 140 | $content = json_encode($this->config, JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING); 141 | break; 142 | case Config::YAML: 143 | $content = yaml_emit($this->config, YAML_UTF8_ENCODING); 144 | break; 145 | case Config::SERIALIZED: 146 | $content = @serialize($this->config); 147 | break; 148 | case Config::ENUM: 149 | $content = implode("\r\n", array_keys($this->config)); 150 | break; 151 | } 152 | @file_put_contents($this->file, $content, LOCK_EX); 153 | return true; 154 | } else { 155 | return false; 156 | } 157 | } 158 | 159 | /** 160 | * @param $k 161 | * @return bool|mixed 162 | */ 163 | public function __get($k) { 164 | return $this->get($k); 165 | } 166 | 167 | /** 168 | * @param $k 169 | * @param $v 170 | */ 171 | public function __set($k, $v) { 172 | $this->set($k, $v); 173 | } 174 | 175 | /** 176 | * @param $k 177 | * @return bool 178 | */ 179 | public function __isset($k) { 180 | return $this->exists($k); 181 | } 182 | 183 | /** 184 | * @param $k 185 | */ 186 | public function __unset($k) { 187 | $this->remove($k); 188 | } 189 | 190 | /** 191 | * @param $key 192 | * @param $value 193 | */ 194 | public function setNested($key, $value) { 195 | $vars = explode(".", $key); 196 | $base = array_shift($vars); 197 | 198 | if (!isset($this->config[$base])) { 199 | $this->config[$base] = []; 200 | } 201 | 202 | $base = &$this->config[$base]; 203 | 204 | while (count($vars) > 0) { 205 | $baseKey = array_shift($vars); 206 | if (!isset($base[$baseKey])) { 207 | $base[$baseKey] = []; 208 | } 209 | $base = &$base[$baseKey]; 210 | } 211 | 212 | $base = $value; 213 | } 214 | 215 | /** 216 | * @param $key 217 | * @param null $default 218 | * @return mixed|null 219 | */ 220 | public function getNested($key, $default = null) { 221 | $vars = explode(".", $key); 222 | $base = array_shift($vars); 223 | if (isset($this->config[$base])) { 224 | $base = $this->config[$base]; 225 | } else { 226 | return $default; 227 | } 228 | 229 | while (count($vars) > 0) { 230 | $baseKey = array_shift($vars); 231 | if (is_array($base) and isset($base[$baseKey])) { 232 | $base = $base[$baseKey]; 233 | } else { 234 | return $default; 235 | } 236 | } 237 | 238 | return $base; 239 | } 240 | 241 | /** 242 | * @param $k 243 | * @param bool $default 244 | * @return bool|mixed 245 | */ 246 | public function get($k, $default = false) { 247 | return ($this->correct and isset($this->config[$k])) ? $this->config[$k] : $default; 248 | } 249 | 250 | /** 251 | * @param $path 252 | * @return array|mixed|null 253 | */ 254 | public function getPath($path) { 255 | $currPath = &$this->config; 256 | foreach (explode(".", $path) as $component) { 257 | if (isset($currPath[$component])) { 258 | $currPath = &$currPath[$component]; 259 | } else { 260 | $currPath = null; 261 | } 262 | } 263 | return $currPath; 264 | } 265 | 266 | /** 267 | * @param $path 268 | * @param $value 269 | */ 270 | public function setPath($path, $value) { 271 | $currPath = &$this->config; 272 | $components = explode(".", $path); 273 | $final = array_pop($components); 274 | foreach ($components as $component) { 275 | if (!isset($currPath[$component])) { 276 | $currPath[$component] = []; 277 | } 278 | $currPath = &$currPath[$component]; 279 | } 280 | $currPath[$final] = $value; 281 | } 282 | 283 | /** 284 | * @param $k 285 | * @param bool $v 286 | */ 287 | public function set($k, $v = true) { 288 | $this->config[$k] = $v; 289 | } 290 | 291 | /** 292 | * @param $v 293 | */ 294 | public function setAll($v) { 295 | $this->config = $v; 296 | } 297 | 298 | /** 299 | * @param $k 300 | * @param bool $lowercase 301 | * @return bool 302 | */ 303 | public function exists($k, $lowercase = false) { 304 | if ($lowercase === true) { 305 | $k = strtolower($k); 306 | $array = array_change_key_case($this->config, CASE_LOWER); 307 | return isset($array[$k]); 308 | } else { 309 | return isset($this->config[$k]); 310 | } 311 | } 312 | 313 | /** 314 | * @param $k 315 | */ 316 | public function remove($k) { 317 | unset($this->config[$k]); 318 | } 319 | 320 | /** 321 | * @param bool $keys 322 | * @return array 323 | */ 324 | public function getAll($keys = false) { 325 | return ($keys === true ? array_keys($this->config) : $this->config); 326 | } 327 | 328 | /** 329 | * @param array $defaults 330 | */ 331 | public function setDefaults(array $defaults) { 332 | $this->fillDefaults($defaults, $this->config); 333 | } 334 | 335 | /** 336 | * @param $default 337 | * @param $data 338 | * @return int|mixed 339 | */ 340 | private function fillDefaults($default, &$data) { 341 | $changed = 0; 342 | foreach ($default as $k => $v) { 343 | if (is_array($v)) { 344 | if (!isset($data[$k]) or !is_array($data[$k])) { 345 | $data[$k] = []; 346 | } 347 | $changed += $this->fillDefaults($v, $data[$k]); 348 | } elseif (!isset($data[$k])) { 349 | $data[$k] = $v; 350 | ++$changed; 351 | } 352 | } 353 | return $changed; 354 | } 355 | 356 | /** 357 | * @param $content 358 | */ 359 | private function parseList($content) { 360 | foreach (explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v) { 361 | $v = trim($v); 362 | if ($v == "") { 363 | continue; 364 | } 365 | $this->config[$v] = true; 366 | } 367 | } 368 | 369 | /** 370 | * @return string 371 | */ 372 | private function writeProperties() { 373 | $content = "#Properties Config file\r\n#" . date("D M j H:i:s T Y") . "\r\n"; 374 | foreach ($this->config as $k => $v) { 375 | if (is_bool($v) === true) { 376 | $v = $v === true ? "on" : "off"; 377 | } elseif (is_array($v)) { 378 | $v = implode(";", $v); 379 | } 380 | $content .= $k . "=" . $v . "\r\n"; 381 | } 382 | return $content; 383 | } 384 | 385 | /** 386 | * @param $content 387 | */ 388 | private function parseProperties($content) { 389 | if (preg_match_all('/([a-zA-Z0-9\-_\.]*)=([^\r\n]*)/u', $content, $matches) > 0) { //false or 0 matches 390 | foreach ($matches[1] as $i => $k) { 391 | $v = trim($matches[2][$i]); 392 | switch (strtolower($v)) { 393 | case "on": 394 | case "true": 395 | case "yes": 396 | $v = true; 397 | break; 398 | case "off": 399 | case "false": 400 | case "no": 401 | $v = false; 402 | break; 403 | } 404 | if (isset($this->config[$k])) { 405 | // todo 406 | } 407 | $this->config[$k] = $v; 408 | } 409 | } 410 | } 411 | } 412 | --------------------------------------------------------------------------------