Обсуждение Статьи Чат-бот-магазин часть 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);
    }

iMakeBots 27.06.2019 в 08:47

Авторизуйтесь через Telegram, чтобы ответить.
Откройте бот @SiteAuthBot, нажмите кнопку Старт/Start. Следуйте инструкциям бота.


Ответы

  • Здравствуйте, спасибо большое за ваши ответы, возвращаясь к первому вопросу
    
                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' => "Описание." ]);
    
    A M 27.06.2019 в 23:40
    • Вопрос 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, "Выводим кабинет");
      }
      iMakeBots 28.06.2019 в 09:11
  • Вставил в начало
    между
    if (array_key_exists("text", $data['message'])) {
    A M 27.06.2019 в 23:59
    • Если вы вставили чужой код, то у вас однозначно не сработает. В том примере, что вы привели выше - скорее всего используют какую-то библиотеку, и в переменной $telegram лежит объект для работы с Телеграм АПИ.

      Соответственно этих переменных в приложении из статьи не объявляли и будет ошибка.
      iMakeBots 28.06.2019 в 09:30
  • Результата соответственно нет. Можно вас попросить написать 2 рабочих с ботом магазина примера (шаблона)? Один с отправкой пользователю документа после нажатия на кнопку, а второй пример с переходом в другой чат после нажатия на кнопку пользователем? Заранее спасибо
    A M 28.06.2019 в 00:00
    • // ...
      $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'
          ]);
      }
      
      iMakeBots 28.06.2019 в 09:44
      • Спасибо вам в очередной раз за развернутые ответы. я вставил ваш код выше и прописал его к $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";
        Сейчас просто всплывает окошко и непонятно, прошел заказ, не прошел заказ, он просто висит на шаге выбора оплаты с кнопками туда/сюда - ни админ, ни пользователь ничего не получают, это так и должно быть?
        A M 28.06.2019 в 21:57
        • Обработку кнопок (для каждой) в приложении из статьи нужно делать в методе
          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, "Заказ оплачен");
              }
          iMakeBots 28.06.2019 в 22:14
          • Здравствуйте, спасибо за ответы. Да, кнопки работают, все стало выводиться, но возникла следующая ситуация. 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";
                        }
            
                /********************************************/
            Спасибо, тоже все работает, но момент вот такой, как реализовать информирование админа (любое, простейшее) хоть в сам телеграм, хоть на почту, хоть еще куда), о том что заказ поступил? Потому что сейчас никаких уведомлений админу не поступает и не понятно есть заказ, нет заказа, существует он вообще. В случае с оплаты через яндекс деньги, реализованной вами по умолчанию, там хотя бы уведомление придет о тому, что поступил платеж на почту, а вообще в целом такие уведомления не приходят, а в случаях если пользователь делая заказ выбрал оплату наличными это очень критично, потому что это довольно казусная ситуация когда человек сидит и чего-то ждет, а о его заказе никто ни слуху ни духу как говориться. можно вариант решения этой незатейливой проблемы? Спасибо.
            A M 29.06.2019 в 20:01
            • Выкладывайте код здесь - это нормально, этот ресурс для этого и сделан, всяко удобнее читать здесь - чем по ссылкам бегать ))

              Обратите внимание на метод, которым я отправляю документ - 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']);
                       // ...
                  }
              iMakeBots 29.06.2019 в 20:16
  • Здравствуйте, не подскажете как добавить новые шаги при составлении заказа? Новые поля я уже добавил
    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;
    
    A M 30.06.2019 в 18:39
    • Оформление заказа в приложение из статьи ограничено определенным количеством запросов данных, чтобы его расширить нужно дописывать логику. В том виде в котором логика сейчас выполнена - расширять не удобно, но можно.

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

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

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

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

      ======
      - Дублируются данные потому что у вас в переменной $text лежат одни и те же данные.
      - Лишние переносы строк, конечно лучше убрать.
      - getNal_ лучше сделайте tinyint(1) по default поставьте 0 (как не наличные), в случае если пользователь укажет за наличные то сменяйте на 1
      iMakeBots 01.07.2019 в 13:59
      • Здравствуйте, спасибо за ответы, в таком случае вы не могли бы меня корректировать если вам не сложно?
        Я правильно понимаю что сначала нужно проделать вот это?
        } 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);
        	  }
        A M 01.07.2019 в 17:45
        • 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',
              ]);
          }
          iMakeBots 02.07.2019 в 08:42
          • Здравствуйте, огромное спасибо. Вы не могли бы посмотреть код (прилично) на ошибки (правильно ли вообще делаю), когда у вас будет свободное время и настроение (не горит), поля добавил бот не стартует. Я просто заметил что вы с утра все время отвечаете, как то неудобно вас отвлекать перед работой или во время работы.
            https://pastebin.com/1eAW77aF
            A M 03.07.2019 в 18:18
            • Для начала очень даже хорошо разобрались, немного видимо запутались в логике - ну это потому что получилось много дублирующего кода, в будущем нужно будет уходить от этого - придет со временем.

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

              https://pastebin.com/Gz9PCwG0
              iMakeBots 04.07.2019 в 11:41
          • Здравствуйте, отправил вам бота, выложите его для всех если хотите как продолжение статьи
            A M 05.07.2019 в 15:47
            • Протестил, исправил пару ошибок - работает.
              iMakeBots 06.07.2019 в 19:54
            • Здравствуйте, у вас удалось довести бота до рабочей версии? У меня проблема, с ботом при заказе не отображает id или наименование заказа. Если есть возможность, поделится рабочей версией бота, был бы очень благодарен. Телеграмм @desctr
              Алексей Никифорович 28.01.2020 в 20:24
            • Здравствуйте. Не могли бы скинуть вашего бота, например в телеграм - @SamielsG. Спасибо.
              Samiels G 03.08.2020 в 23:51
            • Исходник бота
              iMakeBots 04.08.2020 в 10:12
          • Здравствуйте. Подскажите пожалуйста, как сформировать сообщение для админа, чтобы в нём были перечислены товары, их количество и общая сумма...
            // Оформляем заказ
            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 в 17:50
            • Ещё перестали загружаться картинки в товар, во временную базу заносится всё, кроме 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 11.08.2020 в 22:05
            • Кто-нибудь знает почему вдруг перестали добавляться фото к товару? Перепробовал версии 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. да и по мелочи... чел.фак. так сказать... :)

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

              Пример
              iMakeBots 14.08.2020 в 12:03
            • По поводу реализации вывода полного чека админу и пользователю со всеми наименованиями, количеством и итогом - нужно будет создать отдельную публичную функцию?
              Samiels G 15.08.2020 в 10:06
            • По правильному да
              iMakeBots 15.08.2020 в 10:07
    • Можешь выложить рабочий код с оплатой наликом? а то я уже запутался читая километры кода тут в переписке)
      Грачик Абдулошвили 16.08.2020 в 14:41
      • Обновил файл в статье
        Добавил:
        - для админа просмотр заказов /orders, уведомление о новом заказе
        - для пользователя кнопка "оплатить наличными", ссылка на переход в кабинет /lc
        Обновил формирование ссылки на вывод картинки при просмотре товара
        iMakeBots 20.08.2020 в 14:59
        • подскажите, у меня 20 категорий, сейчас они в одну линию что неудобно, можно в три ряда их выстроить вот тут как то-
          // Добавляем кнопки для категорий
          $buttons[][] = $this->buildInlineKeyBoardButton($row['name'], "showUserCategory_" . $row['id']);
          Грачик Абдулошвили 29.08.2020 в 18:21
          • Можно. Для этого надо создать многомерный массив из кнопок.
            iMakeBots 29.08.2020 в 18:35
            • в другом боте я вот так делал и было норм-
              $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]];

              но тут непонятно как так же сделать...
              Грачик Абдулошвили 29.08.2020 в 18:39
            • Принцип тот же просто массив сейчас с одним значением, а вам надо более одной.

              Посмотрите что на выходе получается при отрисовке методом. Там все просто.
              iMakeBots 29.08.2020 в 18:41
            •    $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'])),
              Грачик Абдулошвили 29.08.2020 в 19:06
            • Первый вариант вывода правильный.
              Вам надо перебрать массив категорий и после каждой третьей категории начинать новый вложенный массив

              <?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);
              
              iMakeBots 30.08.2020 в 12:16
            • вот по образцу создал но теперь вообще ничего не выводит-
              http://prntscr.com/u9vwj4
              Грачик Абдулошвили 02.09.2020 в 00:37
  • Отличный Бот, спасибо!.

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