#Пример разработки бота

Облачный Pyrus
Безоблачный Pyrus

В этом разделе мы приводим пример разработки бота с автоответом и согласованием. Также Вы можете скачать готового бота и развернуть его на heroku. Информация будет полезна разработчикам приложений.

Создайте страницу с HTTPS-адресом на своем сайте, например, https://example.com/autoresponse. Эта страница-обработчик будет получать запросы от Pyrus-бота. В обработчик добавьте код по следующей схеме.

  1. Проверяем подпись. Подпись гарантирует, что вызов пришел действительно от Pyrus.

    • Вычисляем подпись тела запроса:

      • python:*
      def _is_signature_correct(message, secret, signature):
          secret = str.encode(secret)
          digest = hmac.new(secret, msg=message, digestmod=hashlib.sha1).hexdigest()
          return hmac.compare_digest(digest, signature.lower())
      
      • php: *
      strtoupper(hash_hmac("sha1", $response_body_as_string, $security_key))
      
      • ruby:*
      OpenSSL::HMAC.hexdigest('sha1', security_key, response_body_as_string).upcase
      

      С#:

      public static Boolean IsSignatureCorrect(String msg, String sig, String secret)
      {
          byte[] hashValue;
          using (HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secret)))
          using (var outStream = new MemoryStream())
          {
              hashValue = hmac.ComputeHash(Encoding.UTF8.GetBytes(msg));
              outStream.Write(hashValue, 0, hashValue.Length);
          }
          var hashString = ToHexString(hashValue);
          return hashString == sig;
      }
      
      public static string ToHexString(IList<byte> hash, bool lowerCase = false)
      {
          if (hash == null)
              throw new ArgumentNullException(nameof(hash));
      
          var chArrayLength = hash.Count * 2;
      
          var chArray = new char[chArrayLength];
          for (int i = 0, index = 0; i < chArrayLength; i += 2, index++)
          {
              var b = hash[index];
              chArray[i] = GetHexValue(b / 16, lowerCase);
              chArray[i + 1] = GetHexValue(b % 16, lowerCase);
          }
      
          return new String(chArray);
      }
      
      private static char GetHexValue(int i, bool lowerCase)
      {
          var startChar = lowerCase ? 'a' : 'A';
          return i < 10
              ? (char) (i + '0')
              : (char) (i - 10 + startChar);
      }
      

      Эти функции производят конвертацию запроса в виде строк в byte array с использованием UTF и вычисляют HMAC-дайджест с использованием алгоритма SHA1 для хеширования.

    • Сравниваем со значением заголовка X-Pyrus-Sig, чтобы удостовериться, что запрос действительно поступил от Pyrus. Если заголовки не совпадают, выполнение останавливаем.

  2. Выбираем значение author.id из полученного объекта ** task** (см. полное описание в разделе Задача по форме с комментариями):

    {
      "id": 5600,
      "text": "New task",
      "create_date": "2016-12-02T11:20:22Z",
      "last_modified_date": "2016-12-02T11:20:22Z",
      "parent_task_id": 0,
      "author": {
        "id": 1731,
        "first_name": "tester",
        "last_name": "papirus",
        "email": "test@pyrus.com"
      },
      "approvals": [[{
        "person": {
            "id": 21913,
            "first_name": "bot",
            "last_name": "papirus",
            "email": "bot@pyrus.com"
          },
          "approval_choice": "waiting"
        }],
        [{
          "person": {
            "id": 1731,
            "first_name": "tester",
            "last_name": "papirus",
            "email": "test@pyrus.com"
          },
        "approval_choice": "waiting"
        }]],
      "comments": [{
        "create_date": "2016-12-02T11:20:22Z",
        "author": {
          "id": 1731,
          "first_name": "tester",
          "last_name": "papirus",
          "email": "test@pyrus.com"
        },
        "approvals_added": [{
          "person": {
            "id": 21913,
            "first_name": "bot",
            "last_name": "papirus",
            "email": "bot@pyrus.com"
          },
          "step": 1
        },
        {
          "person": {
            "id": 1731,
            "first_name": "tester",
            "last_name": "papirus",
            "email": "test@pyrus.com"
          },
          "step": 2
        }],
        "id": 12208
      }]
    }
    

    В примере выше author.email = test@pyrus.com.

  3. Формируем тело ответа:

    {
      "text": "Hello, test@pyrus.com. This task approved by bot.",
      "approval_choice": "approved"
    }
    

    В этом примере:

    • text (необязательный параметр) — текст комментария. Если пропущен, бот сделает комментарий без текста;
    • approval_choice — установка согласования бота. Если согласование не требуется, этот параметр будет проигнорирован сервером. Если бот работает с формой, то этот параметр обязательный;
  4. Возвращаем ответ HTTP 200 ОК и тело, как указано выше.

    Приведем полный пример с кодом бота на Python. Предыдущий пример кода бота с проверкой подписи доступен здесь.

    import hmac
    import hashlib
    import json
    from flask import Flask
    from flask import request
    
    app = Flask(__name__)
    
    @app.route("/", methods=['GET', 'POST'])
    def index():
        body = request.data
        signature = request.headers['x-pyrus-sig']
        secret = 'This-is-bot-secret-key-VtBOEesdOnIcqGaWQeFkWFqyzgdt2PGsZVWBc8h8H0-xU9ux-dN37IjcAqf2pzeqoo5FqdtoH'
        return _prepare_response(body.decode('utf-8'))
    
    def _prepare_response(body):
        task = json.loads(body)["task"]
        task_author = task["author"]["email"]
        return "{{\"text\": \"Hello, {}. This task approved by bot.\", \"approval_choice\": \"approved\"}}".format(task_author)
    
    if __name__ == "__main__":
        app.run("127.0.0.1", 5001) # !!!Не забудьте заменить адрес!!!
    

    Теперь добавьте нового бота и в поле URL впишите адрес обработчика (в нашем примере это https://example.com/autoresponse).

    Для проверки поставьте в Pyrus задачу на бота с запросом его согласования. Убедитесь, что в задаче получен ответ с текстом Approved by bot и стоит согласование бота.