Max: Исключение повторных нажатий кнопок до ответа сервера

Как бы не ругались некоторые товарищи на Habr, что «разрабатывать под Max, себя не уважать», но тем не менее не все имеют этот выбор 😉 Так что проблемы решать таки нужно..

Итак, задача: между нажатием кнопки с кэлбеком и ответом сервера происходит некоторая пауза. Иногда в несколько секунд. Пользователи не всегда дожидаются ответа, и нажимают кнопки по нескольку раз.

Решение: т.к. на основной ответ сервера требуется некоторое время на его подготовку, то перед «настоящим» ответом сервера, отправим «промежуточный», который сообщит пользователю «Подождите ответа сервера», а заодно скроет все кнопки из предыдущего сообщения пользователя, чтоб не было соблазна их нажимать. Код получается примерно такой:

...
    if (isset($in->update_type)){
        if ($in->update_type=="message_callback"){
          $params=[];
          $params["callback_id"]=$in->callback->callback_id;
          $data=[];
          $data["message"]["attachments"]=[];
          $data["notification"]="Подождите ответа сервера...";
          $res=$Max->Reqwest("answers",$params,$data,"POST");    
        };
        if (($in->update_type=="message_created") or ($in->update_type=="message_callback") or ($in->update_type=="bot_started")){                       
          $res=$Api1c->reqwest("MaxCallback2",["body"=>$body]);  // пересылаем всё что пришло в 1С  и получаем ответ
          if ($res==null){
            $params=[];  
            $params["chat_id"]=$in->message->recipient->chat_id;
            $data=[];
            $data["text"]="Временная ошибка серверера. Попробуйте позже.";    
            $data["format"]= "html";            
                $buttons=[];                
                    $button=[];
                    $button["type"]="callback";
                    $button["text"]="Перейти в главное меню";
                    $button["payload"]="Go2MainMenu";                
                $buttons[]=$button;                                
                    $attachment=[];
                    $attachment["type"]="inline_keyboard";
                    $attachment["payload"]["buttons"]=[];
                    $attachment["payload"]["buttons"][]=$buttons;                                                     
            $data["attachments"]=[];    
            $data["attachments"][]=$attachment;
            $res=$Max->Reqwest("messages",$params,$data,"POST");    
            die();  
          };
    // если 1С вернула ошибку то её и показываем пользователю
          if ($res->error==true){
            $params=[];  
            if (isset($in->message)){$params["chat_id"]=$in->message->recipient->chat_id;};
            if (isset($in->chat_id)){$params["chat_id"]=$in->chat_id;};        
            $data=[];
            $data["text"]=$res->result;    
            $data["format"]= "html";            
                $buttons=[];                
                    $button=[];
                    $button["type"]="callback";
                    $button["text"]="Перейти в главное меню";
                    $button["payload"]="Go2MainMenu";                
                $buttons[]=$button;                                
                    $attachment=[];
                    $attachment["type"]="inline_keyboard";
                    $attachment["payload"]["buttons"]=[];
                    $attachment["payload"]["buttons"][]=$buttons;                                                     
            $data["attachments"]=[];    
            $data["attachments"][]=$attachment;
            
            $res=$Max->Reqwest("messages",$params,$data,"POST");          
            die();  
          };
          // Что пришло из 1С, то и отдаём в сообщения пользователю..
          $params=$res->result->params;
          $data=$res->result->data;
          $res=$Max->Reqwest("messages",$params,$data,"POST");          
          die();
...

Разработка бота в мессенджере MAX. Часть 1

Во всех мануалах пишут что бота нужно создавать в боте MasterBot, однако у меня он усиленно пишет что «Сейчас создать бота не получится. Попробуйте позже». Удалось создать только в консоли разработчика из https://business.max.ru/self/#/services, для этого нужно подтвердить организацию при помощи ЭЦП ключа. Простым физическим лицам, похоже пока бота не создать.

Документация по разработке бота тут: https://dev.max.ru/docs-api/ , увы не очень удобная. Как делать некоторые вещи я сделать долго путался, а некоторые так и не удалось, пришлось копаться в исходниках их официального клиента API на Python.

Итак, для начала создал класс для работы с мессенджером, который берет на себя всю рутину:

class TMax {
    public $url="";
    public $login="";
    public $password="";
    public function __construct($url,$token) {
        $this->url=$url;
        $this->token=$token;
    }    

    function Reqwest($reqwest,$params,$type="POST"){
        
        if ($type=="GET"){
           $reqwest=$reqwest."?".http_build_query($params);
        };
        if ($type=="POST"){
           $reqwest=$reqwest."?".http_build_query($params);
        };
        
        
        $ch = curl_init($this->url.$reqwest);
        $authorization = "Authorization:".$this->token;

        $this->PutLog("--(R $type) in $reqwest :".json_encode($params));
        
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json',$authorization)); 
        if ($type=="POST"){
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
        };
        if ($type=="PATCH"){
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));            
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');  
        }
        curl_setopt($ch, CURLOPT_TIMEOUT, 600);
        $res=curl_exec($ch);            

        if(curl_errno($ch)){
            throw new Exception(curl_error($ch));
        };        
        
        $this->PutLog("--(R) out :".json_encode($res));
        $response = json_decode($res);        
        return $response;
        
    }
    
    
    function PutLog($txt){
     if (DEBUG==true){
       $data=Date("m-d-y h:i:s")." ".$txt."\n";  
       file_put_contents(API_LOG_FILE, $data,FILE_APPEND);
       if (DEV_MODE==true){
           echo $data."<br/>";
       };
     };
    }
}

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

  $url="https://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_URL"];
  echo "-оформляем подписку на текущий URL:$url<br/>";  
  $params=[];
  $params["url"]=$url;
  $params["secret"]=secret;
  $params["update_types"]=["message_created","bot_started"];
  $res=$Max->Reqwest("subscriptions",$params);
  if (isset($res->success)){
      if ($res->success==true){
          echo "-Успешно<br/>";  
      } else {
          echo "-возникла ошибка:".$res->message."<br/>";  
      };
  } else {
    echo "-возникла ошибка. Подробности в логах<br/>";  
  };
  die();

Получить информацию о боте можно так:

if (isset($_GET["BotInfo"])){
    echo "-информация о боте<br/>";  
    $params=[];
    $res=$Max->Reqwest("me",$params,"GET");
    if (isset($res->user_id)){
        echo "<pre>";
        var_dump($res);
    } else {
      echo "-возникла ошибка. Подробности в логах<br/>";  
    };
    
    die();  
}

Установить команды бота:

if (isset($_GET["BotCommand"])){
echo "-устанавливаем доступные команды для бота<br/>";
$params=[];
$params[]=["name"=>"Ку","description"=>"Говорить Ку пацаки.."];
$params[]=["name"=>"ЫЫ","description"=>"Говорить ЫЫ пацаки.."];
$params[]=["name"=>"help","description"=>"Тыгыдын"];
$res=$Max->Reqwest("me",["commands"=>$params],"PATCH");
die();
}

Причем установить то я команды установил, но в самом боте в мессенджере они так и не отображаются. Почем? Пока не выяснено.

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

if ($in->update_type=="bot_started"){
  $params=[];
  $params["user_id"]=$in->user->user_id;
  //$params["chat_id"]=$in->chat_id;
  $params["text"]="О! Свежее мясо!";
  $params["format"]="html";
  $res=$Max->Reqwest("messages",$params,"POST");  
};
if ($in->update_type=="message_created"){
  $params=[];
  //$params["user_id"]=$in->message->recipient->user_id;
  $params["chat_id"]=$in->message->recipient->chat_id;
  $params["text"]="Привет человек!";
  $params["format"]="markdown";
  $params["attachments"]=[];
  //$params["link"]=["type"=>"forward","mid"=>$in->message->body->mid];
  $res=$Max->Reqwest("messages",$params,"POST");  
};