← Вернуться к списку тем

Обсуждение Статьи Чат-бот-магазин часть 4

← Начало обсуждения в комментариях статьи

Вопрос 1
Метод - обработчик можно ставить в любое место, но если у вас это будет логически связанное место с другими подобными методами, легче потом искать.

Таймер появляется если от Телеграм ушел на вебхук запрос, то должна быть какая-то реакция-результат от обработчика - вот он и ждет. Например какой-то текст, картинка или промежуточный sendChatAction ("печатает текст" или "отправляет фото" ...).

Также на каждое нажатие inline-кнопки обязательно нужно реагировать отправляя answerCallbackQuery - то есть уведомить Телеграм, что запрос от inline-кнопки пришел, в противном случае Телеграм будет слать повторные запросы, по своему расписанию (через 2 сек, 1 мин, 5 мин, 30 мин ...) пока не получит answerCallbackQuery, это даже если вы уже по своему сценарию все сделали и продолжаете работу, то фоном эти запросы все равно будут приходить.

Вопрос 2
Вместе с сообщением можно отправить только один набор объектов в reply_markup:
1. InlineKeyboardMarkup
2. ReplyKeyboardMarkup
3. ReplyKeyboardRemove
4. ForceReply.

В типовом приложении из статьи при старте отправляется набор объектов inline-кнопок с сылками на категории товаров. Добавить в сообщение еще один набор объектов не получиться, но вы можете отправить перед этим сообщение например с текстом "..." и приложить необходимый набор объектов с клавиатурой.

    private function startBot($chat_id, $data)
    {

        // ...

        $buttons_first[] = [
            $this->buildKeyboardButton("Кабинет"),
            $this->buildKeyboardButton("Помощь"),
        ];
        // отправляем первый набор кнопок
        $this->sendMessage($chat_id, "...", $buttons_first, true);

        // отправляем привет
        $this->sendMessage($chat_id, $text, $buttons);
    }

И для этого надо обновить метод sendMessage

    private function sendMessage($user_id, $text, $buttons = NULL, $type = false)
    {
        // готовим массив данных
        $data_send = [
            'chat_id' => $user_id,
            'text' => $text,
            'parse_mode' => 'html'
        ];
        // если переданны кнопки то добавляем их к сообщению
        if (!is_null($buttons) && is_array($buttons)) {
            $data_send['reply_markup'] = !$type ? $this->buildInlineKeyBoard($buttons) : $this->buildKeyBoard($buttons);
        }
        // отправляем текстовое сообщение
        return $this->botApiQuery("sendMessage", $data_send);
    }

Ответы


  • A M [27.06.2019 в 23:40 → iMakeBots]
    Здравствуйте, спасибо большое за ваши ответы, возвращаясь к первому вопросу
    
                if(!$orderRaw['status']) {
                    // готовим кнопку для перехода в Яндекс.Деньги
                    $url = $this->getUrl($total, $user_id, $orderRaw['id']);
                    $buttons[] = [ $this->buildInlineKeyBoardButton('Оплатить через Яндекс.Деньги', '', $url),
    				$this->buildInlineKeyBoardButton('При отправлении Наличными', 'getNal_' . $orderRaw['id']),
    				$this->buildInlineKeyBoardButton('При отправлении Сканирование QR-Кода Яндекс.Деньги', 'getQrYandex_' . $orderRaw['id']),
    				];
                } else {
                    // если заказ оплачен то уведомляем
                    $text .= "n<b>Заказ оплачен</b>n";
                }
    
    Нашел я в index.php
    
    private function getText($data)
        {
            if ($this->getType($data) == "callback_query") {
                return $data['callback_query']['data'];
            }
            return $data['message']['text'];
        }
    
    Скопировал кусок, вставил в обработчик, результат тот же, абсолютно. таймер висит и все.
    
          public function getNal($data)
        {
            // Получаем id заказа через данные от кнопки
            $param = explode("_", $data['data']);
            $order_id = $param[1];
            if ($this->getNal($data) == "callback_query") {
                return $data['callback_query']['data'];
            }
            // Далее логика - например записать в бд
            // что этот заказ будет оплачен наличными,
            // выслать инструкцию ...
        }
    
        public function getQrYandex($data)
        {
            // Получаем id заказа через данные от кнопки
            $param = explode("_", $data['data']);
            $order_id = $param[1];
            if ($this->getQrYandex($data) == "callback_query") {
                return $data['callback_query']['data'];
     }
            // Далее логика - например записать в бд
            // что этот заказ будет оплачен наличными,
            // выслать инструкцию ...
        }
    
    Есть какое, то решение этой проблемы? С примером текстового сообщения который можно заменить после клика на кнопку оплата наличными, типа "Спасибо" и тд.

    Касательно второго вопроса:

        
    private function startBot($chat_id, $data)
        {
    
            // ...
    
            $buttons_first[] = [
                $this->buildKeyboardButton("Кабинет"),
                $this->buildKeyboardButton("Помощь"),
            ];
            // отправляем первый набор кнопок
            $this->sendMessage($chat_id, "...", $buttons_first, true);
    
            // отправляем привет
            $this->sendMessage($chat_id, $text, $buttons);
        }
    
    Все супер, все работает, шикарно, спасибо большое, но вот такой вопрос, а как продублировать кнопки во второй ряд?
    Я их продублировал, но видно 2 кнопки, за место 4-х. $buttons_twice не выводятся, как быть?
    
        private function startBot($chat_id, $data)
        {
    
            // ...
    
            $buttons_first[] = [
                $this->buildKeyboardButton("Кабинет"),
                $this->buildKeyboardButton("Помощь"),
            ];
            $buttons_twice[] = [
                $this->buildKeyboardButton("Кабинет"),
                $this->buildKeyboardButton("Помощь"),
            ];
            // отправляем первый набор кнопок
            $this->sendMessage($chat_id, "...", $buttons_first, $buttons_twice, true);
    
            // отправляем привет
            $this->sendMessage($chat_id, $text, $buttons);
        }
    
    Вытекающий из кнопок клавиатуры вопрос, как прикрутить к кнопкам функции?
    Нашел статью
    https://habr.com/ru/company/netologyru/blog/326174/
    , взял от туда код кнопок

    
    }elseif ($text == "Картинка") {
                $url = "https://68.media.tumblr.com/6d830b4f2c455f9cb6cd4ebe5011d2b8/tumblr_oj49kevkUz1v4bb1no1_500.jpg";
                $telegram->sendPhoto([ 'chat_id' => $chat_id, 'photo' => $url, 'caption' => "Описание." ]);
            }elseif ($text == "Гифка") {
                $url = "https://68.media.tumblr.com/bd08f2aa85a6eb8b7a9f4b07c0807d71/tumblr_ofrc94sG1e1sjmm5ao1_400.gif";
                $telegram->sendDocument([ 'chat_id' => $chat_id, 'document' => $url, 'caption' => "Описание." ]);
    
  • iMakeBots [28.06.2019 в 09:11 → A M]
    Вопрос 1
    
    public function getNal($data)
        {
            // Получаем id заказа через данные от кнопки
            $param = explode("_", $data['data']);
            $order_id = $param[1];
    
            // 1. Вы пытаетесь вызвать рекурсию getNal в getNal == бесконечность в вашем варианте
            // 2. $data это массив, и $this->getNal($data) == "callback_query" вернет false, 
            // поэтому это условие никогда не сработает
            // 3. В $data['callback_query']['data'] нет такого ключа в $data
            // в $data уже лежит объект ['callback_query'] 
            // Если вам нужно значение из элемента data то оно у нас уже в $param в виде массива
    
            // if ($this->getNal($data) == "callback_query") {
            //    return $data['callback_query']['data'];
            // }
            
            // Для теста вы можете просто вызвать Уведомление
            // answerCallbackQuery это позволяет
            $this->notice($data['id'], "Обработка inline-кнопки getNal", true);
        }
    
        public function getQrYandex($data)
        {
           $this->notice($data['id'], "Обработка inline-кнопки getQrYandex", true);
        }

    Вопрос 2
    Для вывода порядно клавиатуру, также как и inline-кнопки, нужно просто добавить массив объектов следующим элементом массива клавиатуры. Посмотрите какие параметры принимает метод sendMessage, в нем на входе только 1 набор клавиатуры, не пытайтесь направить в него $buttons_twice.

    private function startBot($chat_id, $data)
        {
            // ...
            $buttons_first = [
                [
                    $this->buildKeyboardButton("Кабинет"),
                    $this->buildKeyboardButton("Помощь"),
                ],
                [
                    $this->buildKeyboardButton("Кабинет 1"),
                    $this->buildKeyboardButton("Помощь 1"),
                ],
            ];
    
            // или 
    
            // $buttons_first[0] = [
            //     $this->buildKeyboardButton("Кабинет"),
            //      $this->buildKeyboardButton("Помощь"),
            //  ];
            //  $buttons_first[1] = [
            //      $this->buildKeyboardButton("Кабинет 1"),
            //      $this->buildKeyboardButton("Помощь 1"),
            //  ];
    
            // отправляем первый набор кнопок
            $this->sendMessage($chat_id, "...", $buttons_first, true);
            // ...
        }

    Обработать команды из клавиатуры, можно в методе router
    private function router($data) {
        // ...
        // если это пришел старт бота
        if ($text == "/start") {
            $this->startBot($chat_id, $data);
        } elseif ($text == "Кабинет") {
            // либо вывести информацию здесь, либо вызвать метод который отработает по сценарию
            $this->sendMessage($chat_id, "Выводим кабинет");
            // $this->showCabinet($chat_id, $data); 
        // выводим страницу только админу
        } elseif ($text == "/admin" && $this->isAdmin($chat_id)) {
        // ...
    }
    

    Метод showCabinet для примера выше
    private function showCabinet($chat_id, $data) {
        $this->sendMessage($chat_id, "Выводим кабинет");
    }
  • Toni Joni [04.11.2020 в 09:50 → iMakeBots]
    Спасибо!
  • A M [27.06.2019 в 23:59 → iMakeBots]
    Вставил в начало
    между
    if (array_key_exists("text", $data['message'])) {
  • iMakeBots [28.06.2019 в 09:30 → A M]
    Если вы вставили чужой код, то у вас однозначно не сработает. В том примере, что вы привели выше - скорее всего используют какую-то библиотеку, и в переменной $telegram лежит объект для работы с Телеграм АПИ.

    Соответственно этих переменных в приложении из статьи не объявляли и будет ошибка.
  • A M [28.06.2019 в 00:00 → iMakeBots]
    Результата соответственно нет. Можно вас попросить написать 2 рабочих с ботом магазина примера (шаблона)? Один с отправкой пользователю документа после нажатия на кнопку, а второй пример с переходом в другой чат после нажатия на кнопку пользователем? Заранее спасибо
  • iMakeBots [28.06.2019 в 09:44 → A M]
    // ...
    $buttons[] = [
        $this->buildInlineKeyBoardButton('Другой чат', '', 'https://t.me/imakebots'),
        $this->buildInlineKeyBoardButton('Документ', 'sendDocToUser_0')
    ];
    $this->sendMessage($chat_id, 'Выберите действие', $buttons);
    // ...

    private function sendDocToUser($data) {
        // отсылаем уведомление
        $this->notice($data['id']);
        // отсылаем документ
        $this->botApiQuery('sendDocument', [
            'chat_id' => $this->getChatId($data),
            'document' => 'http://www.pdf995.com/samples/pdf.pdf'
        ]);
    }
    
  • A M [28.06.2019 в 21:57 → iMakeBots]
    Спасибо вам в очередной раз за развернутые ответы. я вставил ваш код выше и прописал его к $buttons_first клавиатуре, результатов ноль. Я про эти кнопки писал.
    http://skrinshoter.ru/s/280619/wDfue06F?a
    как видно из скриншота, кнопки не понимают команды, если не сложно можно решение этой проблемы?
    Касательно обработчика для оплаты наличными, спасибо большое таймер пропал, но я имел ввиду немного другое ,
    
    // Статья 4 - Оплата в Телеграмм
    
        /********************************************/
    
                if(!$orderRaw['status']) {
                    // готовим кнопку для перехода в Яндекс.Деньги
                    $url = $this->getUrl($total, $user_id, $orderRaw['id']);
                    $buttons[] = [ $this->buildInlineKeyBoardButton('Оплатить через Яндекс.Деньги', '', $url),
    				$this->buildInlineKeyBoardButton('Оплата Наличными', 'getNal_' . $orderRaw['id']),
    				$this->buildInlineKeyBoardButton('При получении  Сканирование QR-Кода Яндекс.Деньги', 'getQrYandex_' . $orderRaw['id']),
    				];
                } else {
                    // если заказ оплачен то уведомляем
                    $text .= "n<b>Заказ оплачен</b>n";
                }
    
        /********************************************/
    
    сейчас у вас как видно из скриншота появляется всплывающее окошко,
    http://skrinshoter.ru/s/280619/Zt0fYnAP?a
    я имел ввиду что после клика по инлайнкнопкам "Оплата Наличными" и тд., пользователь получает текстовое уведомление в чат по аналогии с вашим способом оплаты по умолчанию через яндекс деньги и текущий заказ после клика на "Оплата Наличными" был закрыт
    // если заказ оплачен то уведомляем
                    $text .= "n<b>Заказ оплачен</b>n";
    Сейчас просто всплывает окошко и непонятно, прошел заказ, не прошел заказ, он просто висит на шаге выбора оплаты с кнопками туда/сюда - ни админ, ни пользователь ничего не получают, это так и должно быть?
  • iMakeBots [28.06.2019 в 22:14 → A M]
    Обработку кнопок (для каждой) в приложении из статьи нужно делать в методе
    private function router($data)
    Я приводил пример выше постом для кнопки клавиатуры "Кабинет", вам нужно просто добавить свои условия:
    } elseif ($text == "ℹ️Кнопка 1") {
    } elseif ($text == "?Чат с поддержкой") {
    Фразы в условиях должны быть один в один как на кнопках.

    Да, уведомление я привел как пример, вместо него вы можете делать, что угодно, если вы хотите вывести текстовое сообщение просто его отправьте:

    public function getNal($data)
        {
           // высылаем уведомление без сообщения, чтобы 
           // Телеграм понял что мы получили команду от кнопки
           $this->notice($data['id']);
           // отправляем сообщение
           $chat_id = $this->getChatId($data);
           $this->sendMessage($chat_id, "Заказ оплачен");
        }
  • A M [29.06.2019 в 20:01 → iMakeBots]
    Здравствуйте, спасибо за ответы. Да, кнопки работают, все стало выводиться, но возникла следующая ситуация. cм код с пастербин (я решил не засорять вам ресурс лишним кодом, буду на пастербин выкладывать) и скриншоты
    Если все оставить как в вашем случае ничего не меняя
    private function sendDocToUser($data) {
        // отсылаем уведомление
        $this->notice($data['id']);
        // отсылаем документ
        $this->botApiQuery('sendDocument', [
            'chat_id' => $this->getChatId($data),
            'document' => 'http://www.pdf995.com/samples/pdf.pdf'
        ]);
    }
    то все выводиться нормально
    http://skrinshoter.ru/s/290619/EOfeyAIw?a
    , но если подставить свои данные
    https://pastebin.com/yE2jR7L9
    https://pastebin.com/xLZ7c5wa
    то уже отображается по другому
    http://skrinshoter.ru/s/290619/1qRO2KuS?a
    также ссылки на документ автоматически стали выводиться при создании заказа сами по себе
    http://skrinshoter.ru/s/290619/5mMHyNyl?a
    как решить эти моменты? и как сделать переход в другой чат по клику на кнопку (сейчас он присылает ссылку на чат, если нажать кнопку).

    и опять вопрос насчет метода оплаты
    // Статья 4 - Оплата в Телеграмм
    
        /********************************************/
    
                if(!$orderRaw['status']) {
                    // готовим кнопку для перехода в Яндекс.Деньги
                    $url = $this->getUrl($total, $user_id, $orderRaw['id']);
                    $buttons[] = [ $this->buildInlineKeyBoardButton('Оплатить через Яндекс.Деньги', '', $url),
    				$this->buildInlineKeyBoardButton('Оплата Наличными', 'getNal_' . $orderRaw['id']),
    				$this->buildInlineKeyBoardButton('При получении  Сканирование QR-Кода Яндекс.Деньги', 'getQrYandex_' . $orderRaw['id']),
    				];
                } else {
                    // если заказ оплачен то уведомляем
                    $text .= "n<b>Заказ оплачен</b>n";
                }
    
        /********************************************/
    Спасибо, тоже все работает, но момент вот такой, как реализовать информирование админа (любое, простейшее) хоть в сам телеграм, хоть на почту, хоть еще куда), о том что заказ поступил? Потому что сейчас никаких уведомлений админу не поступает и не понятно есть заказ, нет заказа, существует он вообще. В случае с оплаты через яндекс деньги, реализованной вами по умолчанию, там хотя бы уведомление придет о тому, что поступил платеж на почту, а вообще в целом такие уведомления не приходят, а в случаях если пользователь делая заказ выбрал оплату наличными это очень критично, потому что это довольно казусная ситуация когда человек сидит и чего-то ждет, а о его заказе никто ни слуху ни духу как говориться. можно вариант решения этой незатейливой проблемы? Спасибо.
  • iMakeBots [29.06.2019 в 20:16 → A M]
    Выкладывайте код здесь - это нормально, этот ресурс для этого и сделан, всяко удобнее читать здесь - чем по ссылкам бегать ))

    Обратите внимание на метод, которым я отправляю документ - sendDocument, а вы пытаетесь отправить его текстовым сообщением sendMessage, он вам ссылку текстом и присылает - все правильно отрабатывает.
        $this->botApiQuery('sendDocument', [
            'chat_id' => $this->getChatId($data),
            'document' => 'http://www.pdf995.com/samples/pdf.pdf'
        ]);
    ======
    Ссылку по кнопке я приводил пример, посмотрите выше - где документ по кнопке прилетает.
    ======
    Уведомление админа можно, сделать в методе setReady
        /** Оформляем заказ
         * @param $data
         */
        private function setReady($data)
        {
            // ...
                    // удаляем из корзины
                    $delBasket = $this->pdo->prepare("DELETE FROM bot_shop_basket WHERE user_id = :user_id");
                    $delBasket->execute(['user_id' => $user_id]);
    
                    // уведомляем админа
                    $this->sendMessage($this->admin, "Новый заказ поступил - №" . $parent_id);
    
                    // переадресовать в личный кабинет
                    $this->notice($data['id']);
                    $this->userLc($user_id, $data['message']['message_id']);
             // ...
        }
  • A M [30.06.2019 в 18:39 → iMakeBots]
    Здравствуйте, не подскажете как добавить новые шаги при составлении заказа? Новые поля я уже добавил
    https://pastebin.com/v09tQiB4
    http://skrinshoter.ru/s/280619/Q92pXBLC?a
    И как сделать так чтоб каждая запись, присваивалась своему полю?
    потому что, сейчас написанное пользователем на этапе внесения адреса автоматически дублируется в другие поля
    http://skrinshoter.ru/s/280619/15Wbgunl?a
    а как сделать так чтоб после полей "телефон" и "адрес", можно было добавить к примеру поле "комментарий к заказу" (введите комментарий и тд.) следующим шагом и/или другие поля, свои поля (к примеру, поле1, поле2 и тд.)? Можно какой нибудь шаблон который можно копипастить заменяя "поле1" на свое какое нибудь поле? Спасибо.

            
                        $text_ .= "nТелефон: " . $text;
                        $text_ .= "nnАдрес: ";
                        $text_ .= "nnnНовое поле 1:";
                        $text_ .= "nnnnНовое поле 2:";
                        $text_ .= "nnnnnНовое поле 3:";
                        $text_ .= "nnnnnnНовое поле 4:";
    
    я так понимаю что в данном случае лишнии n можно убрать? они как отступ выступают или как перенос на новую строку?

    Чтоб в бд добавить данные о платежных системах, достаточно импортировать данные код в таблицу?

    CREATE TABLE IF NOT EXISTS 'данные о платежных системах' (
      'id' int(11) NOT NULL AUTO_INCREMENT,
      'name' varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
      'getNal_' varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
    'getQrYandex_' varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
      PRIMARY KEY ('id')
    ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
    
  • iMakeBots [01.07.2019 в 13:59 → A M]
    Оформление заказа в приложение из статьи ограничено определенным количеством запросов данных, чтобы его расширить нужно дописывать логику. В том виде в котором логика сейчас выполнена - расширять не удобно, но можно.

    В других приложениях я использую логику в которой данные для оформления заказа берутся из модели, в ней в массиве указаны какие поля запрашивать, формат данных у полей, описание полей, обязательно к заполнению или нет и т.д. - и расширить форму заказа можно просто дополнив этот массив не нарушая работу логики - это даже делается самим клиентом через удобный веб-интерфейс.

    Для того, чтобы добавить обработку новых полей в приложении из статьи, вы можете по аналогии с предыдущими методами добавить свои, алгоритм используйте тот же:

    1. сначала идет запрос на ввод данных Экран "Укажите данные для нового поля 1"
    2. запись в бд пользователю, каким методом нужно обработать следующее сообщение "newField_1",
    3. когда пользователь отправит сообщение роутер проверит наличии записи и перенаправит в нужный метод
    3. метод newField обработает сообщение, запишит в бд заказа
    4. выведет запрос на ввод данных Экран "Укажите данные для нового поля 2"
    5. сменить запись в бд пользователю на "newField_2"
    6. и тд. ...

    Писать готовое решение не буду, для этого потребуется много времени.

    ======
    - Дублируются данные потому что у вас в переменной $text лежат одни и те же данные.
    - Лишние переносы строк, конечно лучше убрать.
    - getNal_ лучше сделайте tinyint(1) по default поставьте 0 (как не наличные), в случае если пользователь укажет за наличные то сменяйте на 1
  • A M [01.07.2019 в 17:45 → iMakeBots]
    Здравствуйте, спасибо за ответы, в таком случае вы не могли бы меня корректировать если вам не сложно?
    Я правильно понимаю что сначала нужно проделать вот это?
    } elseif (preg_match("~^step_1_phone$~", $actionUser)) {
                            // если ждем данные для добавления телефона
                            $this->savePhoneUser($text, $data);
                        } elseif (preg_match("~^step_2_adress$~", $actionUser)) {
                            // если ждем данные для добавления адреса
                            $this->saveAdressUser($text, $data);
                        } elseif (preg_match("~^step_3_newfield1$~", $actionUser)) {
                            // если ждем данные для добавления адреса
                            $this->savenewfield1User($text, $data);
                        } elseif (preg_match("~^step_4_newfield2$~", $actionUser)) {
                            // если ждем данные для добавления адреса
                            $this->savenewfield2User($text, $data);
                        } elseif (preg_match("~^step_5_newfield3$~", $actionUser)) {
                            // если ждем данные для добавления адреса
                            $this->savenewfield3User($text, $data);
                        } elseif (preg_match("~^step_6_newfield4$~", $actionUser)) {
                            // если ждем данные для добавления адреса
                            $this->savenewfield4User($text, $data);

    Затем 4 раза повторить данный код?
    private function insertnewfield1($user_id, $data)
        {
            $text = "<b>Оформление заказа</b>nn";
            // сумма заказа
            $text .= "Сумма заказа: " . $this->totalSumOrder($user_id) . " рублей";
            $text_ .= "nТелефон: " . $phone->fetch()['phone'];
            $text_ .= "nАдрес: " . $adress->fetch()['adress'];
            // инструкция
            $text .= "nnУкажите свой телефон в формате  79001234567:";
            // отправляем данные
            $this->botApiQuery("editMessageText", [
                'chat_id' => $user_id,
                'text' => $text,
                'message_id' => $this->getMessageId($data),
                'parse_mode' => 'html',
            ]);
            // глушим уведомление
            $this->notice($data['id']);
        }
    

    Как быть на этом моменте? Как перейти в следующий шаг newfield1?
    private function saveAdressUser($text, $data)
        {
            $user_id = $this->getChatId($data);
            // Достаем телефон
            $phone = $this->pdo->prepare("SELECT phone FROM bot_shop_profile WHERE user_id = :user_id");
            $phone->execute(['user_id' => $user_id]);
    
            if ($this->setActionUser("step_7_ready", $user_id)) {
                if ($this->setParamUser('adress', $text, $user_id)) {
                    $text_ = "<b>Оформление заказа</b>nn";
                    // сумма заказа
                    $text_ .= "Сумма заказа: " . $this->totalSumOrder($user_id) . " рублей";
                    // телефон
                    $text_ .= "nТелефон: " . $phone->fetch()['phone'];
                    $text_ .= "nАдрес для доставки: " . $text;
                    $buttons[][] = $this->buildInlineKeyBoardButton('✔ Готово', 'setReady_0');
                } else {
                    $text_ = "Ошибка попробуйте снова /start";
                }
            } else {
                $text_ = "Ошибка попробуйте еще раз";
            }
            // готовим данные
            $data_send = [
                'chat_id' => $user_id,
                'text' => $text_,
                'parse_mode' => 'html',
            ];
            // если есть кнопки добавляем
            if (is_array($buttons)) {
                $data_send['reply_markup'] = $this->buildInlineKeyBoard($buttons);
            }
            // отправляем запрос
            $this->botApiQuery("sendMessage", $data_send);
        }
    Это правильный код?
    private function saveReceiveName($user_id, $data)
    	  {
    		$user_id = $this->getChatId($data);
    		// Достаем телефон
                    $phone = $this->pdo->prepare("SELECT phone FROM bot_shop_profile WHERE user_id = :user_id");
                    $phone->execute(['user_id' => $user_id]);
    		// Достаем адрес
                    $adress = $this->pdo->prepare("SELECT adress FROM bot_shop_profile WHERE user_id = :user_id");
                    $adress->execute(['user_id' => $user_id]);
                    if ($this->setActionUser("step_3_receivename", $user_id)) {
    		    if ($this->setParamUser('receivename', $text, $user_id)) {
    			 $text_ = "Оформление заказа\n\n";
                             // сумма заказа
                             $text_ .= "Сумма заказа: " . $this->totalSumOrder($user_id) . " рублей";
                             // телефон
                             $text_ .= "\nТелефон отправителя: " . $phone->fetch()['phone'];
    			$text_ .= "\nАдрес отправителя: " . $adress->fetch()['adress'];
    			$text_ .= "\nИмя получателя: " . $text;
    		} else {
                            $text_ = "Ошибка попробуйте снова /start";
                    }
                } else {
                    $text_ = "Ошибка попробуйте еще раз";
                }
            } else {
                $text_ = "Ошибка в веденных данных, попробуйте еще раз.\n\nУкажите имя получателя в формате Имя и Фамилия:";
            }
            $this->botApiQuery("sendMessage", [
                'chat_id' => $user_id,
                'text' => $text_,
                'parse_mode' => 'html',
            ]);
    		// отправляем запрос
            $this->botApiQuery("sendMessage", $data_send);
    	  }
  • iMakeBots [02.07.2019 в 08:42 → A M]
    private function saveAdressUser($text, $data) {
        $user_id = $this->getChatId($data);
        // Достаем телефон
        $phone = $this->pdo->prepare("SELECT phone FROM bot_shop_profile WHERE user_id = :user_id");
        $phone->execute(['user_id' => $user_id]);
    
        // !!!!!!!!!!!!!!!!!!
        // устанавливаем пользователю следующий шаг ДЛЯ НОВОГО ПОЛЯ 1
        if ($this->setActionUser("step_3_newfield1", $user_id)) {
    
    
        	// если удалось сохранить адрес
            if ($this->setParamUser('adress', $text, $user_id)) {
                // готовим текст сообщения
                $text_ = "<b>Оформление заказа</b>nn";
                // сумма заказа
                $text_ .= "Сумма заказа: " . $this->totalSumOrder($user_id) . " рублей";
                // телефон
                $text_ .= "nТелефон: " . $phone->fetch()['phone'];
                $text_ .= "nАдрес для доставки: " . $text;
    
                // !!!!!!!!!!!!!!!!!!
                // кнопки здесь не нужны, нужно сделать запрос данных
                // >> Затем 4 раза повторить данный код?
                // нет того кода повторять не надо - все запросы нужно делать после проверки предудущих значений
                // !!!!!!!!!!!!!!!!!!
    
                $text_ .= "nnУкажите данные для нового поля 1";
                // $buttons[][] = $this->buildInlineKeyBoardButton('✔ Готово', 'setReady_0');
    
    
            } else {
                $text_ = "Ошибка попробуйте снова /start";
        	}
        } else {
                $text_ = "Ошибка попробуйте еще раз";
        }
        // готовим данные
        $data_send = [
            'chat_id' => $user_id,
            'text' => $text_,
            'parse_mode' => 'html',
        ];
        // если есть кнопки добавляем
        if (is_array($buttons)) {
            $data_send['reply_markup'] = $this->buildInlineKeyBoard($buttons);
        }
        // отправляем запрос
        $this->botApiQuery("sendMessage", $data_send);
    }



    >> Это правильный код?


    private function step_3_newfield1($text, $data) {
        $user_id = $this->getChatId($data);
        // Достаем данные
        $data = $this->pdo->prepare("SELECT phone, adress FROM bot_shop_profile WHERE user_id = :user_id");
        $data->execute(['user_id' => $user_id]);
        $data_ = $data->fetch();
    
        if ($this->setActionUser("step_4_newfield2", $user_id)) {
                if ($this->setParamUser('receivename', $text, $user_id)) {
                    $text_ = "Оформление заказаnn";
                    // сумма заказа
                    $text_ .= "Сумма заказа: " . $this->totalSumOrder($user_id) . " рублей";
                    // телефон
                    $text_ .= "nТелефон отправителя: " . $data_['phone'];
                    // адрес
                    $text_ .= "nАдрес отправителя: " . $data_['adress'];
                    // присланное значение для поля 1
                    $text_ .= "nИмя получателя: " . $text;
    
                    // запрос данных для поля 2
                    $text_ .= "nnУкажите данные для нового поля 2";
    
                } else {
                    $text_ = "Ошибка попробуйте снова /start";
            }
        } else {
            $text_ = "Ошибка попробуйте еще раз";
        }
        
        $this->botApiQuery("sendMessage", [
            'chat_id' => $user_id,
            'text' => $text_,
            'parse_mode' => 'html',
        ]);
    }
  • A M [03.07.2019 в 18:18 → iMakeBots]
    Здравствуйте, огромное спасибо. Вы не могли бы посмотреть код (прилично) на ошибки (правильно ли вообще делаю), когда у вас будет свободное время и настроение (не горит), поля добавил бот не стартует. Я просто заметил что вы с утра все время отвечаете, как то неудобно вас отвлекать перед работой или во время работы.
    https://pastebin.com/1eAW77aF
  • iMakeBots [04.07.2019 в 11:41 → A M]
    Для начала очень даже хорошо разобрались, немного видимо запутались в логике - ну это потому что получилось много дублирующего кода, в будущем нужно будет уходить от этого - придет со временем.

    Подправил без тестирования. Скинете весь код бота (через бот обратной связи) - попробую отладить.

    https://pastebin.com/Gz9PCwG0
  • A M [05.07.2019 в 15:47 → iMakeBots]
    Здравствуйте, отправил вам бота, выложите его для всех если хотите как продолжение статьи
  • iMakeBots [06.07.2019 в 19:54 → A M]
    Протестил, исправил пару ошибок - работает.
  • Алексей Никифорович [28.01.2020 в 20:24 → A M]
    Здравствуйте, у вас удалось довести бота до рабочей версии? У меня проблема, с ботом при заказе не отображает id или наименование заказа. Если есть возможность, поделится рабочей версией бота, был бы очень благодарен. Телеграмм @desctr
  • Samiels G [03.08.2020 в 23:51 → A M]
    Здравствуйте. Не могли бы скинуть вашего бота, например в телеграм - @SamielsG. Спасибо.
  • iMakeBots [04.08.2020 в 10:12 → A M]
  • Samiels G [11.08.2020 в 17:50 → iMakeBots]
    Здравствуйте. Подскажите пожалуйста, как сформировать сообщение для админа, чтобы в нём были перечислены товары, их количество и общая сумма...
    // Оформляем заказ
    private function setReady($data){
    	$user_id = $this->getChatId($data);
    	$basket = $this->pdo->prepare("SELECT * FROM bot_shop_basket WHERE user_id = :user_id");
    	$basket->execute(['user_id' => $user_id]);
    	$user = $this->pdo->prepare("SELECT * FROM bot_shop_profile WHERE user_id = :user_id");
    	$user->execute(['user_id' => $user_id]);
    if ($basket->rowCount() > 0) {
    	$userInfo = $user->fetch();
    	$inOrder = $this->pdo->prepare("INSERT INTO bot_shop_order SET user_id = :user_id, date = NOW(), status = 0, name = :name, phone = :phone, adress = :adress, comm = :comm");
    if ($inOrder->execute([
    	'user_id' => $user_id,
    	'name' => trim($userInfo['first_name'] . " " . $userInfo['last_name']),
    	'phone' => $userInfo['phone'],
    	'adress' => $userInfo['adress'],
    	'comm' => $userInfo['comm'],
    ])) {
    	$parent_id = $this->pdo->lastInsertId();
    while ($item = $basket->fetch()) {
    	$inOrderProduct = $this->pdo->prepare("INSERT INTO bot_shop_order_product SET parent_id = :parent_id, product_id = :product_id,product_count = :product_count");
    	$inOrderProduct->execute(['parent_id' => $parent_id, 'product_id' => $item['product_id'], 'product_count' => $item['product_count']]);
    }
    	$delBasket = $this->pdo->prepare("DELETE FROM bot_shop_basket WHERE user_id = :user_id");
    	$delBasket->execute(['user_id' => $user_id]);
    	$this->sendMessage($this->admin, "<b>Поступил новый заказ - №" . $parent_id . "</b>nn<i>Общая сумма: </i>₸n<i>Пользователь: </i><b>".$userInfo['first_name']." ".$userInfo['last_name']."</b>n<i>Адрес: </i><b>".$userInfo['adress']."</b>n<i>Телефон: </i><b>".$userInfo['phone']."</b>n<i>Комментарий: </i><b>".$userInfo['comm']."</b>"); // уведомляем админа
    	$this->notice($data['id']);
    	$this->userLc($user_id, $data['message']['message_id']);
    } else {
    	$this->notice($data['id'], "Ошибка_", true);
    }
    } else {
    	$this->notice($data['id'], "Ошибка", true);
    }
    }
    И было бы очень здорово показать весь заказ клиенту, перед тем, как он его подтвердит, а вдруг чего-то забыл...
  • Samiels G [11.08.2020 в 22:05 → Samiels G]
    Ещё перестали загружаться картинки в товар, во временную базу заносится всё, кроме image_tlg и image и выводится сообщение "Ошибка при добавлении товара img1". img1 - переименоно из 1 в addProductPhoto
    private function addProductPhoto($category, $product, $file_id){
    	$checkHref = $this->pdo->prepare("SELECT * FROM bot_shop_product_temp WHERE id = :id"); // запрос на проверку есть ли такая временная запись товара в базе
    	$checkHref->execute(['id' => $product]); // выполняем запрос
    if ($checkHref->rowCount() !== 0) { // если вернулось ноль строк
    	$photo = $this->getPhoto($file_id); // получаем картинку
    if ($photo) {
    	$insert = $this->pdo->prepare("INSERT INTO bot_shop_product SET parent = :parent, name = :name, description = :description, price = :price, unit = :unit, image_tlg = :image_tlg, image = :image, hide = 1"); // Добавляем в основную таблицу новый товар
    	$item = $checkHref->fetch(); // достаем временные данные
    	$array = [ // готовим данные
    		'parent' => $category,
    		'name' => $item['name'],
    		'description' => $item['description'],
    		'price' => $item['price'],
    		'unit' => $item['unit'],
    		'image_tlg' => $file_id,
    		'image' => $photo];
    if ($insert->execute($array)) { // если удалось добавить товар
    	$this->cleareTempProduct(); //очищаем временную таблицу
    	$this->adminActionCancel(); // очищаем действия админа
    	$catName = $this->pdo->prepare("SELECT name FROM bot_shop_category WHERE id = :id"); // название категории
    	$catName->execute(['id' => $category]);
    	$fields = $this->prepareCategory($catName->fetch()['name'], $category); // выводим список товаров
    	$this->botApiQuery("sendMessage", $fields); // обновляем сообщение - выводим список товаров
    } else {$this->sendMessage($this->admin, "Ошибка при добавлении товара img0");
    	$buttons[] = [$this->buildInlineKeyBoardButton("Отменить", "addProductCancel_" . $category . "_" . $product),];}
    } else {$this->sendMessage($this->admin, "Ошибка при добавлении товара img1");
    	$buttons[] = [$this->buildInlineKeyBoardButton("Отменить", "addProductCancel_" . $category . "_" . $product),];}
    } else {$this->sendMessage($this->admin, "Ошибка при добавлении товара img2");
    	$buttons[] = [$this->buildInlineKeyBoardButton("Отменить", "addProductCancel_" . $category . "_" . $product),];}
    }
    private function getPhoto($file_id){
    	$file_path = $this->getPhotoPath($file_id); // получаем file_path
    return $this->copyPhoto($file_path); // возвращаем результат загрузки фото
    }
    private function getPhotoPath($file_id){
    	$array = $this->botApiQuery("getFile", ['file_id' => $file_id]); // получаем объект File
    return $array['result']['file_path']; // возвращаем file_path
    }
    private function copyPhoto($file_path){
    	$file_from_tgrm = "https://api.telegram.org/file/bot" . $this->token . "/" . $file_path; // ссылка на файл в телеграме
    	$ext = end(explode(".", $file_path)); // достаем расширение файла
    	$name_our_new_file = $this->img_path . "/" . time() . "." . $ext; // назначаем свое имя - время_в_секундах.расширение_файла
    return copy($file_from_tgrm, $name_our_new_file) ? $name_our_new_file : false; // возвращаем путь картинки или false
    }
    
    версия php 7.4.4, но ведь работало же... Ранее загруженные картинки выводятся нормально, а новые загружаться не хотят... в какую сторону копать?
  • Samiels G [12.08.2020 в 15:43 → Samiels G]
    Кто-нибудь знает почему вдруг перестали добавляться фото к товару? Перепробовал версии PHP с 5.3 и до 7.4.6 везде одно и тоже. Может API изменили?
    Ошибки были найдены и устранены, всё оказалось так,: бот был продублирован из корня в каталог "бот", т.к. в корне осталась папка с картинками, то они спокойно вытаскивались, а при записи - в логе была найдена ошибка о неверной ссылке на директорию с картинками. При бота в каталог был изменён пут к "имг" на "бот/имг" - просмотр так же возможен, но записать он не может, смена пути на "имг" проблему решило, но стал не доступен просмотр. Решено - в строке $text .= "​​\n";} / был изменён на /bot/. Ещё свежий PHP ругается на некоторые строки, например в
    
    // Было
    private function copyPhoto($file_path){
    	$file_from_tgrm = "https://api.telegram.org/file/bot" . $this->token . "/" . $file_path;
    	$ext = end(explode(".", $file_path));
    	$name_our_new_file = $this->img_path . "/" . time() . "." . $ext;
    return copy($file_from_tgrm, $name_our_new_file) ? $name_our_new_file : false;
    }
    // Исправлено
    private function copyPhoto($file_path){
    	$file_from_tgrm = "https://api.telegram.org/file/bot" . $this->token . "/" . $file_path;
    	$expl = explode(".", $file_path);
    	$ext = end($expl);
    	$name_our_new_file = $this->img_path . "/" . time() . "." . $ext;
    return copy($file_from_tgrm, $name_our_new_file) ? $name_our_new_file : false;
    }
    
    пришлось добавить переменную $expl. да и по мелочи... чел.фак. так сказать... :)

    По поводу реализации вывода полного чека админу и пользователю со всеми наименованиями, количеством и итогом - нужно будет создать отдельную публичную функцию?
  • iMakeBots [13.08.2020 в 09:40 → Samiels G]
    В логах есть ошибки?
  • Samiels G [13.08.2020 в 19:34 → Samiels G]
    А возможно отправлять админ-уведомление сразу нескольким людям с разными ID?
    Если да, то как указать несколько получателей?
  • iMakeBots [14.08.2020 в 12:03 → Samiels G]
    Уведомление можно направить например всем пользователя из какого-нибудь определенного массива. Для этого обойти массив и направить им всем.

    Пример
  • Samiels G [15.08.2020 в 10:06 → Samiels G]
    По поводу реализации вывода полного чека админу и пользователю со всеми наименованиями, количеством и итогом - нужно будет создать отдельную публичную функцию?
  • iMakeBots [15.08.2020 в 10:07 → Samiels G]
    По правильному да
  • Грачик Абдулошвили [16.08.2020 в 14:41 → A M]
    Можешь выложить рабочий код с оплатой наликом? а то я уже запутался читая километры кода тут в переписке)
  • iMakeBots [20.08.2020 в 14:59 → Грачик Абдулошвили]
    Обновил файл в статье
    Добавил:
    - для админа просмотр заказов /orders, уведомление о новом заказе
    - для пользователя кнопка "оплатить наличными", ссылка на переход в кабинет /lc
    Обновил формирование ссылки на вывод картинки при просмотре товара
  • Грачик Абдулошвили [29.08.2020 в 18:21 → iMakeBots]
    подскажите, у меня 20 категорий, сейчас они в одну линию что неудобно, можно в три ряда их выстроить вот тут как то-
    // Добавляем кнопки для категорий
    $buttons[][] = $this->buildInlineKeyBoardButton($row['name'], "showUserCategory_" . $row['id']);
  • iMakeBots [29.08.2020 в 18:35 → Грачик Абдулошвили]
    Можно. Для этого надо создать многомерный массив из кнопок.
  • Грачик Абдулошвили [29.08.2020 в 18:39 → iMakeBots]
    в другом боте я вот так делал и было норм-
    $inline_keyboard = [[$inline_button1,$inline_button2,$inline_button3],[$inline_button4,$inline_button5,$inline_button6],[$inline_button7,$inline_button8,$inline_button9],[$inline_button10,$inline_button11,$inline_button12],[$inline_button13,$inline_button14,$inline_button15],[$inline_button16,$inline_button17,$inline_button18],[$inline_button19,$inline_button20,$inline_button21],[$inline_button22,$inline_button23,$inline_button24]];

    но тут непонятно как так же сделать...
  • iMakeBots [29.08.2020 в 18:41 → iMakeBots]
    Принцип тот же просто массив сейчас с одним значением, а вам надо более одной.

    Посмотрите что на выходе получается при отрисовке методом. Там все просто.
  • Грачик Абдулошвили [29.08.2020 в 19:06 → iMakeBots]
       $buttons[] = [
                    $this->buildInlineKeyBoardButton($row['name'], "showCategory_" . $row['id']),
                    $this->buildInlineKeyBoardButton($row['name'], "showCategory_" . $row['id']),
                    $this->buildInlineKeyBoardButton($row['name'], "showCategory_" . $row['id']),
                ];
    в таком виде у меня просто дублируются 3 колонки.

    может типа такого что то надо мутить-
     $this->buildInlineKeyBoardButton(($row['name'], "showUserCategory_" . $row['id']),($row['name'], "showUserCategory_" . $row['id'])),
  • iMakeBots [30.08.2020 в 12:16 → iMakeBots]
    Первый вариант вывода правильный.
    Вам надо перебрать массив категорий и после каждой третьей категории начинать новый вложенный массив

    <?php
    
    $arr = [1,2,3,4,5,6,7,8,9,0];
    $new_arr = [];
    $row = 0;
    
    foreach($arr as $key => $val) {
        if($key % 3 == 0) {
            $row  += 1;
        }
        $new_arr[$row][] = $val;
    }
    
    echo "<pre>";
    print_r($new_arr);
    
  • Грачик Абдулошвили [02.09.2020 в 00:37 → iMakeBots]
    вот по образцу создал но теперь вообще ничего не выводит-
    http://prntscr.com/u9vwj4
  • Грачик Абдулошвили [21.04.2020 в 12:46 → iMakeBots]
    Отличный Бот, спасибо!.

    Выложите кто нибудь этого же бота но с уже рабочей функцией ОПЛАТА наликом и уведомлением админа о поступлении заказа.
    спасибо!