0.0.24 • Published 2 years ago

vue-3-twilio-chat v0.0.24

Weekly downloads
-
License
-
Repository
-
Last release
2 years ago

This is the Vue 3 version of here and has been created as a plugin on npm.

Project Setup

npm install

Compile and Hot-Reload for Development

npm run dev

Type-Check, Compile and Minify for Production

npm run build

working locally

You will need to create a Twilio trial account here and add Flex chat via the console. Make a copy of the package/.env.example file and rename it .env.local (this will not be tracked by git). Edit the file to set the required environment variables.

VariableLocationNotes
VUE_APP_ACCOUNT_SIDTwilio ConsoleLook for ACCOUNT SID under Project Info.
VUE_APP_FLEX_FLOW_SIDFlex ConsoleLook for flexFlowSid under WEBCHAT
VUE_APP_TWILIO_RUNTIME_DOMAINDeployed ServiceDomain exposed after creating a Twilio service containing serverless functions. Read section below twilio function.
VITE_APP_TWILIO_COLOR_SCHEMEcheck tailwind.css for colour schemes

Sending a message should create a task which will become viewable on the Flex Agent Desktop.

Notes

This package uses the Twilio Chat client library to interface with Twilio's APIs. Documentation is available here (use the "Classes" dropdown at the top of the page).

The Programmable Chat API will reach EOL on 2022/07/25, to be superseded by the Twilio Conversations API. This may or may not affect this package. The Twilio Conversations client library API is extremely similar to the Chat library (at time of writing), so migration should hopefully be straightforward if necessary.

Consuming the plugin

To use this component in your app head over to here and install as a dependency.

In your Vue component/app

import { TheChat } from '' into your vue page.
import 'twilio-chat-with-vue-3/styles.css' into your main file.

The component accepts four required props, see the table above for where to find the values. If the color-scheme prop is missing the app will default to the root colours.

<TheChat
  account-sid=""
  flex-flow-sid=""
  runtime-domain="https://..."
  color-scheme="tertiary"
></TheChat>
[twilio function] you will need to create a function on the twilio console. Go to functions > services > create service (I have created 'test-function'). Add a new function endpoint (/endChat) and make it public. Copy the code below into it, this is to use for the endChat function. In envirionment variables create FLEX_CHAT_SERVICE_SID . Save and deploy.
exports.handler = async function (context, event, callback) {
  // Start off defining all of our constants
  const client = context.getTwilioClient();
  const chatServiceSid = context.FLEX_CHAT_SERVICE_SID; // note: This must be set in your .env

  const response = new Twilio.Response(); // This is what will be in the eventual HTTP response via the callback method
  const responseBody = {
    success: false,
    payload: {
      errors: [],
    },
  }; // and this will be the Body of the response

  response.appendHeader('Content-Type', 'application/json');
  response.appendHeader('Access-Control-Allow-Origin', '*');
  response.appendHeader('Access-Control-Allow-Methods', 'OPTIONS POST');
  response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');

  // Throwing everything in a try/catch block to handle errors
  try {
    if (Object.keys(event).length === 0) {
      // This handles the case where NO parameters were sent, allowing for empty Options request (since we don't have access to the Request method/headers)
      throw {
        status: 200,
        code: 60200,
        message: 'No body sent.',
      };
    }

    if (!event.channelSid) {
      // We're missing our parameter! Throw an exception early.
      throw {
        status: 400,
        code: 60200,
        message: 'Request must include a channelSid',
      };
    }

    // Build our updates array for Promise handling
    let updatePromises = [];

    // First pull the Channel via the Twilio SDK
    let channel = await client.chat
      .services(chatServiceSid)
      .channels(event.channelSid)
      .fetch(); // If this channel isn't found, it will throw a 404 error

    // if channel.attributes fails to parse, it'll throw a SyntaxError
    let channelAttributes = JSON.parse(channel.attributes);

    // Build the update object for the Chat Channel
    channelAttributes.status = 'INACTIVE';
    let channelUpdate = {
      attributes: JSON.stringify(channelAttributes),
    }; // All we need to do for the Chat Channel is set 'status' to 'INACTIVE'

    // and add the update to our promises array
    updatePromises.push(
      client.chat
        .services(chatServiceSid)
        .channels(event.channelSid)
        .update(channelUpdate),
    );

    // There are several cases where we may not have a taskSid populated – it is
    // the /expected/ case at any time before we enqueue in TaskRouter, but
    // could also mean that the populateChatChannelWithTaskSid function is
    // improperly implemented/configured if it happens after enqueuing. Because
    // of this, we must allow for this state without error but we'll print a
    // warning message to help with troubleshooting.
    if (channelAttributes.taskSid) {
      console.warn(
        "No task sid on chat channel attribtues – this may be fine, but if things aren't working correctly, check your populateChatChannelWithTaskSid function.",
      );
      // Now let's pull the Task
      let task = await client.taskrouter
        .workspaces(context.TWILIO_WORKSPACE_SID)
        .tasks(channelAttributes.taskSid)
        .fetch(); // If this task isn't found, it'll throw a 404 error

      if (
        ['canceled', 'completed', 'wrapping'].includes(task.assignmentStatus)
      ) {
        // This task is already complete! Probably some sort of race condition, but throw a 200 status since that's what we were here do to anyway
        throw {
          status: 200,
          code: 45007,
          message: `Task SID ${channelAttributes.taskSid} is already in ${task.assignmentStatus} state.`,
        };
      }

      // Build the update object for the Task
      let taskUpdate = {
        assignmentStatus: task.assignmentStatus,
      };
      switch (task.assignmentStatus) {
        case 'pending':
        case 'reserved':
          taskUpdate.assignmentStatus = 'canceled';
          taskUpdate.reason = 'Customer ended chat.';
          break;
        case 'assigned':
          taskUpdate.assignmentStatus = 'wrapping';
      } // Move the Task into either the "canceled" or "wrapping" state, depending on its previous state

      // and add the update to our promises array
      updatePromises.push(
        client.taskrouter
          .workspaces(context.TWILIO_WORKSPACE_SID)
          .tasks(channelAttributes.taskSid)
          .update(taskUpdate),
      );
    }

    // Now ensure that all promises return successful
    await Promise.all(updatePromises);
    responseBody.success = true;
    responseBody.payload.message = 'Chat ended.';
  } catch (e) {
    // We've caught an error! Handle the HTTP error response
    console.error(e.message || e);

    response.setStatusCode(e.status || 500);

    responseBody.success = false;
    responseBody.payload.errors = responseBody.payload.errors || [];
    responseBody.payload.errors.push({
      code: e.code || 500,
      message: e.message,
    });
  }

  response.setBody(responseBody);
  return callback(null, response);
};
0.0.24

2 years ago

0.0.23

2 years ago

0.0.22

2 years ago

0.0.21

2 years ago

0.0.20

2 years ago

0.0.19

2 years ago

0.0.18

2 years ago

0.0.17

2 years ago

0.0.16

2 years ago

0.0.15

2 years ago

0.0.14

2 years ago

0.0.13

2 years ago

0.0.12

2 years ago

0.0.11

2 years ago

0.0.10

2 years ago

0.0.9

2 years ago

0.0.8

2 years ago

0.0.7

2 years ago

0.0.6

2 years ago

0.0.4

2 years ago

0.0.3

2 years ago

0.0.2

2 years ago

0.0.1

2 years ago