Adam Bezecny
Adam Bezecny

Reputation: 265

Programatically created tasks attributes not displayed in Twilio Flex

I am experimenting with Twilio Flex. What we are trying to build is chat(bot) application integrating two or three participants (user + cc human agent + chabot versus user + cc human agent).

I am able to join Twilio chat channel (both from web application based on Node.js started app https://www.twilio.com/docs/chat/javascript/quickstart and our server side code utilizing npm package twilio-chat). Then I am creating Flex Task using the following code:

let task = await twilioClient.taskrouter.workspaces(TWILIO_FLEX_WORKSPACE)
  .tasks.create({attributes: JSON.stringify({
    type: 'support',
    message: 'some message from chat here'
  }),
  workflowSid: TWILIO_FLEX_CHAT_WORKFLOW,
  taskChannel: 'chat'
})

Task is successfully created and pops up in Twilio Flex agent dashboard but when I accept it:

  1. CHAT tab is completely empty

  2. INFO tab contains only general information, e.g.:

    TASK CONTEXT
    Task type
    chat
    
    Task created on
    Wed Jan 23 2019 16:01:36 GMT+0100 (Central Europe Standard Time)
    
    Task priority
    0
    
    Task queue
    Everyone
    
    CUSTOMER CONTEXT
    Customer name / phone number
    Anonymous
    
    Country
    ADDONS
    No add-ons enabled. To expand your experience, visit Twilio Marketplace
    

my custom attributes (type/message) are not included at all

I was not able to find any complex example of using flex with twilio chat, just very generic (and not overly explanatory) high level overview here: https://www.twilio.com/docs/taskrouter/contact-center-blueprint/chat-taskrouter

Has somebody experience with integrating chat (not necessarily Twilio Chat) with Twilio Flex?

  1. How to create Task Rotuer task properly so that message is displayed in chat and custom attributes are visible on INFO tab?
  2. How to achieve that agent's response in Flex chat window will be routed back to existing chat?
  3. How to achieve subsequent user messages will be routed into existing Flex Task and not create new task instead? In other words how to keep the track of conversations between chat and Flex?

  4. Anybody having code snippet showing how original "Flex Create Chat" Twilio function looked like before it was removed from Twilio console and replaced with Twilio Proxy Service integration?

Upvotes: 2

Views: 1734

Answers (1)

makazone
makazone

Reputation: 21

I'm trying to do something similar (Programmable Chat, Twilio Flex) and here is what got me to a working state:

  1. Instead of creating a task manually I opted-in for a default Studio flow that you get when initializing Flex. I started with this doc but it only gives a theoretical understanding. I figured out the actual webhook by using Twilio Flex Sample Web App and tracing channels it creates.
  2. Whenever a web/mobile client wants to chat I call my custom endpoint to perform the needed setup.
// After creating a Twilio Programmable Chat Channel set up a Studio webhook 

app.post("/create-task", function(request, response) {
  // see full code below

  chatService.channels
    .create({
      // See full code below
    })
    .then(channel => {
      const webhookUrl = channel.links.webhooks;

      const options = {
        url: webhookUrl,
        method: "POST",
        auth: {
          user: accountSid,
          pass: authToken
        },
        form: {
          Type: "studio",
          "Configuration.FlowSid": twilioFlowSid
        }
      };

      return new Promise((resolve, reject) => {
        requestLibrary.post(options, function(error, response, body) {
          if (!error) {
            resolve(channel);
          } else {
            reject(error);
          }
        });
      });
    });
  // see full code below
});
  1. That only gets you halfway. By now you should see a task popping up in Flex but if Agent accepts it he can't message back nor does he see messages from the client. I assume there is some magic involved when you do it from Twilio Widget.

I was able to figure out that for channels I create manually when I accept a task as an Agent the Agent doesn't join a Channel. So the number of participants is 1 instead of 2.

This means that Agent basically can't see Channel data and send messages to it. Maybe my Channel is missing some metadata in attributes but I wasn't able to figure it out yet.

What I did is I used a callback that you get whenever the task status changes and in particulary, I used reservation.accepted event to add the Agent as a member of a channel manually. You can add callback under TaskRouter Settings at the bottom.

app.post("/accept-task-callback", function(request, response) {
  const { TaskAttributes, WorkerSid, WorkerName, EventType } = request.body;
  const { channelSid } = JSON.parse(TaskAttributes);

  console.log("received event", EventType);

  if (EventType !== "reservation.accepted") {
    response.send("OK");
    return;
  }

  console.log("Adding member", WorkerSid, WorkerName, "on event", EventType);

  chatService
    .channels(channelSid)
    .members.create({ identity: WorkerName })
    .then(member => {
      response.send({
        instruction: "accept"
      });
    })
    .catch(error => {
      console.error(error);
      response.send({
        instruction: "reject"
      });
    });
});

Here is a full code

// This is Express App

app.post("/create-task", function(request, response) {
  const accountSid = process.env.TWILIO_ACCOUNT_SID;
  const authToken = process.env.TWILIO_AUTH_TOKEN;
  const workspaceSid = process.env.TWILIO_WORKSPACE_SID;
  const workflowSid = process.env.TWILIO_WORKFLOW_SID;
  const twilioFlowSid = "FW..."

  // Identity of the Twilio Programmable Chat user who initiates a dialog. You get it from you signin or whatever
  const username = request.body || "Nancy Drew Support";

  chatService.channels
    .create({
      type: "private",
      friendlyName: username,
      attributes: JSON.stringify({
        status: "ACTIVE",
        from: username,
        channel_type: "web"
      })
    })
    .then(channel => {
      const webhookUrl = channel.links.webhooks;

      const options = {
        url: webhookUrl,
        method: "POST",
        auth: {
          user: accountSid,
          pass: authToken
        },
        form: {
          Type: "studio",
          "Configuration.FlowSid": twilioFlowSid
        }
      };

      return new Promise((resolve, reject) => {
        requestLibrary.post(options, function(error, response, body) {
          if (!error) {
            resolve(channel);
          } else 
            reject(error);
          }
        });
      });
    })
    .then(async channel => {
        // Join as a Customer requesting Support
      return chatService
        .channels(channel.sid)
        .members.create({ identity: username });
    })
    .then(member => {
        // return back channel sid we created
      response.send({ channelSid: member.channelSid });
    })
    .catch(error => {
      console.log(error);
      response.fail(error);
    });
});

app.post("/accept-task-callback", function(request, response) {
  const { TaskAttributes, WorkerSid, WorkerName, EventType } = request.body;
  const { channelSid } = JSON.parse(TaskAttributes);

  console.log("received event", EventType);

  if (EventType !== "reservation.accepted") {
    response.send("OK");
    return;
  }

  console.log("Adding member", WorkerSid, WorkerName, "on event", EventType);

  chatService
    .channels(channelSid)
    .members.create({ identity: WorkerName })
    .then(member => {
      response.send({
        instruction: "accept"
      });
    })
    .catch(error => {
      console.error(error);
      response.send({
        instruction: "reject"
      });
    });
});

I still have a lot to figure out. Unfortunately, Flex documentation isn't great at this point and misses some very basic tutorials like "Configuring Twilio Programmable Chat with Flex from ground up". This kind of tutorials would help everyone to understand how all Twilio APIs come together in such a powerful tool like Flex.

Anyway, hope my answer helps. I can try to elaborate further if you have questions.

Upvotes: 2

Related Questions