Saltar a contenido

Conectar con un Webhook

Veremos la manera como se puede integrar el bot a un webhook para obtener información que necesite para el correcto funcionamiento de su bot.

Info

Si usted usa java puede utilizar el JDK de Yanibot disponible.

 repositories {
    mavenLocal()
    mavenCentral()

    maven {
        url "https://gitlab.com/api/v4/groups/7124687/-/packages/maven"
        name "GitLab"
        credentials(HttpHeaderCredentials) {
            name = 'Deploy-Token'
            value = 'QjiTxyiYti7ecTeCdJt8'
        }
        authentication {
            header(HttpHeaderAuthentication)
        }
    }
}


dependencies {
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    implementation 'group.jedai.yani:yani-bot-sdk:0.9.3'
}

Cuando definimos un webhook en yani evalua si la URL es absoluta ejecuta directamente, si es relativa toma la propiedad Remote Url y la concatena para formar el url completo.

La ejecución del webhook no puede tomar más de 3 segundos, caso contrario Yani, cancela su ejecución.

Ejemplo Skill doPedido

En el Skill doPedido se llama a un WebHook el cual es un método POST y su URL: /doPedido. Para realizar la integración con un servicio externo recuerde que debe tener configurado los parámetros generales del bot específicamente los campos pertenecientes a Duración Token, Base Url, Base Url Remoto.

En /doPedido recibirá como RequestBody un objeto WebHookBody el cual puede ser interpretado como una estructura JSON.

 public class WebHookBody {
    private ObjectNode memory;
    private String text;
    private int red;
    private String baseUrl;
    private Map<String, String> params = new HashMap<>();
    private List<ExtraParam> extras = new ArrayList<>();
    private List<Pos> pos = new ArrayList<>();
}
 {
   "memory":{
      "memory1":"value1",
      "memory2":"value2",
   },
   "text":"",
   "red":999,
   "baseUrl":"http://url/api/v1",
   "params":{
      "params1":"value1",
      "params2":"value2",
   },
   "extras":[],
   "pos":[],
 }

Este objeto debe utilizarlo para obtener los campos necesarios para guardar el pedido en un servicio externo o local de ser el caso.

Parámetro Descripción
memory Aquí puede obtener la variables que el bot a recolectado a lo largo del flujo y que usted etiqueto en cada skill en memoria
text Almacena el último texto ingresado por el cliente
red Representa la red del canal por el cual esta interactuando el cliente
baseUrl baseUrl a cual debe realizar la petición esto está configurado en parámetros generales como Base Url Remoto.
params Lista de items definidos en la sección de Prámetros generales del bot, como tiempo de sesión, etc
extras Parámetros extras definidos que son necesarios para realizar peticiones a servicios, vienen un merge con los parámetros cifrados, por tanto los keys de parámetros extras y cifrados no pueden ser iguales parámetros extras.
pos lista de TAGS directamente enviados desde bot-core contiene los tokens etiquetados que han sido reconocidos por el NLP

Info

Agregue un parámetro encriptado denominado wh-enc Si usted desea que Yanibot enviee un encabezado X-YANI-SIGNATURE que valide la integridad del payload mendiante el uso de HmacSHA256 con clave compartida

Para nuestro ejemplo lo que recibimos es:

{
   "memory":{
      "cedula":"1234567890",
      "direccion":"Av Libertad",
      "extras":"queso",
      "intentos_login":0,
      "name":"Demo",
      "nombre":"Cliente apellido",
      "pedido":"Chivito de pollo",
      "start":false
   },
   "text":"Cliente apellido",
   "red":999,
   "baseUrl":"http://bot-jedai:8090/response",
   "params":{
      "maxAttempt":"3",
      "msgAttempt":"Ha excedido el número de intentos",
      "skillAttempt":"5dacc590fca136000196ffae"
   },
   "extras":[
      {
         "key":"user",
         "val":"demoUser",
         "enc":false,
         "val2":""
      }
   ],
   "pos":[
      {
         "token":"Cliente",
         "pos":"PROPN",
         "index":0
      },
      {
         "token":"apellido",
         "pos":"PROPN",
         "index":0
      }
   ]
}

Obtendremos la información necesaria para crear la petición que nos permita crear el pedido por lo que tendremos que obtener de memory los campos que creamos necesarios así como de extras los parámetros extras que fueron definidos.

Crearemos el pedido el cual necesita la cédula del cliente, el nombre del cliente, la dirección del cliente y el producto que solicita el cliente, y adicional necesitamos el usuario.

Info

Si usted usa java con el JDK de yanibot disponible puede utilizar YaniUtil.getParam() donde puede obtener de manera facil el parametro que necesita.

public WebHookResponse doPedido(WebHookBody body) {
    var actions = new ArrayList<Accion>();
    try {
        var cedula = body.getMemory().get("cedula").asText();
        var direccion = body.getMemory().get("direccion").asText();
        var extras = body.getMemory().get("extras").asText();
        var nombre = body.getMemory().get("nombre").asText();
        var pedido = body.getMemory().get("pedido").asText();
        var user = YaniUtil.getParam(body, "user");
        HttpHeaders headers = new HttpHeaders();
        headers.add("user", user);
        var bodyReq = mapper.createObjectNode();
        bodyReq.put("cedula", cedula);
        bodyReq.put("direccion", direccion);
        bodyReq.put("extras", extras);
        bodyReq.put("name", nombre);
        bodyReq.put("pedido", pedido);
        HttpEntity<?> request = new HttpEntity<>(bodyReq, headers);
        var resp = restTemplate.exchange(body.getBaseUrl() + "/pedido/doPedido", POST, request, ObjectNode.class).getBody();
        if (resp != null && resp.get("done").asBoolean()) {
            body.getMemory().put("numPedido", resp.get("numPedido").asInt());
        } else {
            String text = "${name} por el momento nos encontramos trabajando para brindarte un mejor servicio. Pronto volveremos";
            actions.add(Accion.builder().type(ActionType.TEXT).text(Collections.singletonList(text)).build());
        }
    } catch (Exception ex) {
        log.error("doPedido", ex);
        String text = "${name} por el momento nos encontramos trabajando para brindarte un mejor servicio. Pronto volveremos";
        actions.add(Accion.builder().type(ActionType.TEXT).text(Collections.singletonList(text)).build());
    }
    return WebHookResponse.builder().actions(actions).memory(body.getMemory()).build();
}

Se realiza la petición POST con los parámetros necesarios y esta nos devolverá como resultado.

{
  "id":"5f8b6382d8487405c83a7ea9",
  "valor":5.15,"done":true,
  "numPedido":2,
  "cedula":"1234567890",
  "nombre":"Cliente apellido",
  "direccion":"Av Libertad",
  "producto":"CHIVITO DE POLLO",
  "extra":"QUESO"
}

con el resultado obtenido podremos crear la respuesta que espera el bot este es un objeto WebHookResponse el cual puede ser interpretado como una estructura JSON, vamos a añadir a la memoria el numPedido que espera para poder visualizarlo al cliente, así como las acciones que se requieran.

{
  "actions":[], 
  "memory":{
    "cedula":"1234567890",
    "direccion":"Av Libertad",
    "extras":"queso",
    "intentos_login":0,
    "name":"Demo",
    "nombre":"Cliente apellido",
    "pedido":"Chivito de pollo",
    "start":false,
    "numPedido":2
  }, 
  "opciones":[]
}

consulPedido

En /consulPedido recibiremos el objeto WebHookBody el cual puede ser interpretado como JSON, el cual presenta esta información.

{
   "memory":{
      "cedula":"1234567890",
      "direccion":"Av Libertad",
      "extras":"queso",
      "intentos_login":0,
      "name":"Demo",
      "nombre":"Cliente apellido",
      "pedido":"Chivito de pollo",
      "numPedidoIn": 2,
      "start":false
   },
   "text":2,
   "red":999,
   "baseUrl":"http://bot-jedai:8090/response",
   "params":{
      "maxAttempt":"3",
      "msgAttempt":"Ha excedido el número de intentos",
      "skillAttempt":"5dacc590fca136000196ffae"
   },
   "extras":[
      {
         "key":"user",
         "val":"demoUser"
      }
   ],
   "pos":[]
}
Aquí solo necesitaremos obtener el numPedido que deseamos consultar.

public WebHookResponse viewPedido(WebHookBody body) {
    var actions = new ArrayList<Accion>();
    try {
        var numPedidoIn = body.getMemory().get("numPedidoIn").asText();
        var resp = restTemplate.getForEntity(body.getBaseUrl() + "/pedido?numPedido=" + numPedidoIn, ObjectNode.class).getBody();
        if (resp != null) {
        String text = "El pedido ${numPedidoIn} para " + resp.get("nombre").asText()
                    + ", en la dirección " + resp.get("direccion").asText()
                    + " se encuentra en camino.";
        actions.add(Accion.builder().type(ActionType.TEXT).text(Collections.singletonList(text)).build());
        } else {
            String text = "No se encontro un pedido para ${numPedidoIn}.";
            actions.add(Accion.builder().type(ActionType.TEXT).text(Collections.singletonList(text)).build());
        }
    } catch (Exception ex) {
        log.error("consulPedido", ex);
        String text = "${name} por el momento nos encontramos trabajando para brindarte un mejor servicio. Pronto volveremos";
        actions.add(Accion.builder().type(ActionType.TEXT).text(Collections.singletonList(text)).build());
    }
    return WebHookResponse.builder().actions(actions).memory(body.getMemory()).build();
}

Se realiza la petición GET con los parámetros necesarios y esta nos devolverá como resultado.

{
  "id":"5f8b6382d8487405c83a7ea9",
  "valor":5.15,"done":true,
  "numPedido":2,
  "cedula":"1234567890",
  "nombre":"Cliente apellido",
  "direccion":"Av Libertad",
  "producto":"CHIVITO DE POLLO",
  "extra":"QUESO"
}

Acction text

con el resultado obtenido crearemos el objeto WebHookResponse el cual puede ser interpretado como una estrucutra JSON, aqui solo crearemos una acción tipo TEXT con la información necesaria.

{
  "actions":[
    {
      "order":0, 
      "type":"TEXT", 
      "dsc":null, 
      "activo":false, 
      "text":["El pedido ${numPedidoIn} para Cliente apellido, en la dirección Av Libertad se encuentra en camino."],
      "url":null, 
      "method":null, 
      "auth":false, 
      "user":null, 
      "pass":null, 
      "headers":[], 
      "title":null, 
      "subtitle":null, 
      "imageUrl":null, 
      "buttons":[], 
      "carousel":[], 
      "red":[]
    }
  ], 
  "memory":{
    "cedula":"1234567890",
    "direccion":"Av Libertad",
    "extras":"queso",
    "intentos_login":0,
    "name":"Demo",
    "nombre":"Cliente apellido",
    "pedido":"Chivito de pollo",
    "numPedidoIn":2,
    "start":false
  }, 
  "opciones":[]
}

Formatos de acciones

A continuación se enlista el formato de tipos de acciones a inclurse como items de la propiedad Array actions

Action card

{
  "type": "CARD",
  "title": "titulo imágen",
  "subtitle": null,
  "imageUrl": "https://www.cinemascomics.com/wp-content/uploads/2020/06/poder-darth-vader-960x720.jpg",
  "buttons": [
    {
      "type": "POSTBACK",
      "title": "boton 1",
      "payload": "4QqgloWV93OXRylZU22V3oEWMcIOJ6qY"
    },
    {
      "type": "POSTBACK",
      "title": "boton 2",
      "payload": "ZhmKj+xfr/qtXlL5KC6ERmpZLwm6CE4p"
    },
    {
      "type": "POSTBACK",
      "title": "boton 3",
      "payload": "4h4BBp/zwM9p8Oz0N1BEXYXG/14N98Vk"
    }
  ]
}
{
  "type": "CAROUSEL",
  "carousel": [
    {
      "type": "CARD",
      ....
    }
  ]
}

Action Buttons

{
  "type": "BUTTONS",
  "title": "titulo botones",
  "subtitle": null,
  "buttons": [
    {
      "type": "POSTBACK",
      "title": "boton 1",
      "payload": "4QqgloWV93OXRylZU22V3oEWMcIOJ6qY"
    },
    {
      "type": "POSTBACK",
      "title": "boton 2",
      "payload": "ZhmKj+xfr/qtXlL5KC6ERmpZLwm6CE4p"
    },
    {
      "type": "WEB_URL",
      "title": "boton 3",
      "link": "https://starwars.com/"
    }
  ]
}