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

Облачный 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 bool IsSignatureCorrect(string msg, string sig, string secret)
    {
      if (msg is null)
        throw new ArgumentNullException(nameof(msg));
      if (secret is null)
        throw new ArgumentNullException(nameof(secret));
    
      byte[] hashValue;
      using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secret)))
      {
        hashValue = hmac.ComputeHash(Encoding.UTF8.GetBytes(msg));
      }
    
      return ToHexString(hashValue) == sig;
    }
    
    private static string ToHexString(IReadOnlyList<byte> hash)
    {
      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);
        chArray[i + 1] = GetHexValue(b % 16);
      }
    
      return new string(chArray);
    }
    
    private static char GetHexValue(int i)
    {
      return i < 10
        ? (char)('0' + i)
        : (char)('A' + i - 10);
    }
    

    Эти функции производят конвертацию запроса в виде строк в 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 и стоит согласование бота.

Была ли эта статья полезной?