Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Building with Chat Attachments (Webchat 2.0)


(information)

Info

This guide is for Flex UI 1.x.x and channels that use Programmable Chat and Proxy. If you are using Flex UI 2.x.x or you are starting out, we recommend that you build with Webchat 3.0.

Chat attachments allow agents and customers to send non-text content, like images, PDFs, and videos using Flex Webchat. While you can do a lot of cool stuff using chat attachments, you'll need to create rules and safeguards to keep agents safe, customers happy, and your business secure. Fortunately, chat attachments were built in a customizable way. In the following code samples, you can see some of the strategies the Twilio team has cooked up to implement some common scenarios.

(information)

Info

There are many other ways to solve these problems, and your problems will likely be more complex than the scenarios presented here. These code samples are meant to be a starting point to help familiarize you with some of the tools you have available - so definitely tweak these to fit your preferred tooling and patterns!


Use Programmable Chat and the Flex UI to delete messages

use-programmable-chat-and-the-flex-ui-to-delete-messages page anchor

Each chat attachment is associated with a Message in the Programmable Chat API. To delete a file which has been sent, you can therefore delete the message using the remove method provided by Programmable Chat SDK.


_10
// Add a delete button. to every MessageListItem
_10
_10
const DeleteMessage = ({ message }) => (
_10
// message is the default prop passed through by the MessageListItem
_10
<button type="button" onClick={() => message.source.remove()}>
_10
delete
_10
</button>
_10
);
_10
_10
Flex.MessageListItem.Content.add(<DeleteMessage key="delete-message" />, { sortOrder: -1 });


Replace Actions to upload files to personal storage

replace-actions-to-upload-files-to-personal-storage page anchor

By default, chat attachments uses Twilio Chat's Media Resource for file storage. To use your own storage for message attachments, you'll need to replace the SendMediaMessage action, which is triggered when a media file is sent.

You'll need to:

  1. Upload the file to our own private storage
  2. Pass on the unique identifier to the replaced sendMessage Action in messageAttributes
  3. Trigger a regular sendMessage action.

_56
// Implement personal storage
_56
_56
const uploadFileToMyStorage = async (file) => {
_56
const formData = new FormData();
_56
formData.append("image", file);
_56
_56
// Upload the file to private storage
_56
const res = await fetch("https://api.imgur.com/3/image", {
_56
method: "POST",
_56
headers: new Headers({
_56
Authorization: "Client-ID 546c25a59c58ad7"
_56
}),
_56
body: formData
_56
});
_56
return res.json();
_56
};
_56
_56
// Replace the action
_56
Flex.Actions.replaceAction("SendMediaMessage", async (payload: Flex.ActionPayload) => {
_56
const { file, messageAttributes, channelSid } = payload;
_56
_56
// Retrieve the uploaded file location
_56
const res = await uploadFileToMyStorage(file);
_56
_56
// Include the new media file when sending the message
_56
return Flex.Actions.invokeAction("SendMessage", {
_56
messageAttributes: {
_56
...messageAttributes,
_56
media: {
_56
url: res.data.link,
_56
filename: file.name,
_56
contentType: file.type,
_56
size: file.size
_56
}
_56
},
_56
body: file.name,
_56
channelSid
_56
});
_56
});
_56
_56
// Now you need to render your uploaded file. First, delete the body of the MessageBubble. Then, add a new body, including appropriate HTML that points to your uploaded file (in this example, an image tag is sufficient for rendering the image. Don't forget your alt text!)
_56
_56
// Create new message bubble content
_56
const PersonalStorageContent = ({ message }) => (
_56
<div>
_56
<img src={message.source.attributes.media.url) alt=”file uploaded from custom storage” style={{ width: "100%" }} />
_56
</div>
_56
);
_56
_56
Flex.MessageBubble.Content.remove("body", {
_56
if: (props) => !!props.message.source.attributes.media
_56
});
_56
_56
Flex.MessageBubble.Content.add(<PersonalStorageContent key="message-bubble-body" />, {
_56
if: (props) => !!props.message.source.attributes.media
_56
});


Use before Action hooks to filter content and check for viruses

use-before-action-hooks-to-filter-content-and-check-for-viruses page anchor

Depending on your use case, content filtering and virus checking can be done in different ways, but most implementations will involve the before action hooks. For example, you can use it to download the file after content filtering, or run the downloaded file against a set of guidelines before it gets sent as a message.


_46
// Check file content
_46
_46
Flex.Actions.addListener("beforeDownloadMedia", async (payload, cancelAction) => {
_46
_46
const { message } = payload;
_46
_46
const url = await message.media.getContentUrl();
_46
_46
// Validate file before download (note that checkFileContent method needs to be written)
_46
_46
const result = await checkFileContent(url);
_46
_46
if (!result.pass) {
_46
_46
// Failed to validate content of the file
_46
_46
cancelAction();
_46
_46
}
_46
_46
});
_46
_46
Flex.Actions.addListener("beforeSendMediaMessage", async (payload, cancelAction) => {
_46
const { file } = payload;
_46
_46
// Validate file before sending
_46
const result = await checkFileContent(file);
_46
_46
if (!result.pass) {
_46
// Failed to validate content of the file
_46
cancelAction();
_46
}
_46
});
_46
_46
Flex.Actions.addListener("beforeAttachFile", async (payload, cancelAction) => {
_46
_46
const { file } = payload;
_46
_46
// Validate file before attaching
_46
const result = await checkFileContent(file);
_46
_46
if (!result.pass) {
_46
// Failed to validate content of the file
_46
cancelAction();
_46
}
_46
});


Use Programmable Chat to change permissions of inappropriate users

use-programmable-chat-to-change-permissions-of-inappropriate-users page anchor

Sometimes a chat user can send inappropriate files or messages and as an agent you might want to block this user from sending messages or media messages. This can be done using programmable chat rest api.

All users in chat are associated with a Role and Permissions. To block users, you'll want to create a blockedUser Role. Based on your needs, remove the sendMessage and/or sendMediaMessage permissions from the new Role

In order to block somebody, you can update the Role of their Member Resource using a Twilio Function. Provide the new blockedUser SID as the roleSid parameter. Check out the guide on using Functions from your Plugin for additional support.

Twilio Function Code


_19
exports.handler = function(context, event, callback) {
_19
// Use context parameter to initialize Twilio client and retrieve stored environment variables
_19
const twilioClient = context.getTwilioClient();
_19
const chatServiceSid = context.CHAT_SERVICE_SID; // // Get Chat service sid from https://www.twilio.com/console/chat/dashboard
_19
const blockedUserSid = context.BLOCKED_USER_SID
_19
_19
_19
// Use the event parameter to retrieve dynamic information, like the current chat channel and the member to blockedUserSid
_19
const {chatChannelSid, memberSid} = event
_19
_19
console.log(event)
_19
_19
twilioClient.chat.services(chatServiceSid)
_19
.channels(chatChannelSid)
_19
.members(memberSid)
_19
.update({roleSid: blockedUserSid})
_19
.then(member => callback(null, member.sid))
_19
.catch(err => callback(err, null))
_19
};

Plugin Code


_31
// Create function to block user
_31
const blockUser = (chatChannelSid, memberSid) => {
_31
const body = { chatChannelSid, memberSid };
_31
_31
// Set up the HTTP options for your request
_31
const options = {
_31
method: 'POST',
_31
body: new URLSearchParams(body),
_31
headers: {
_31
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
_31
}
_31
};
_31
_31
// Make the network request using the Fetch API
_31
fetch('https://YOUR_DOMAIN.twil.io/block-user', options)
_31
.then(resp => resp.json())
_31
.then(data => console.log(data));
_31
}
_31
}
_31
_31
// Create a button component to block users
_31
const BlockUserButton = (props) => (
_31
<button type="button" onClick={() => blockUser(props.channelSid, props.member.source.sid)}>
_31
block
_31
</button>
_31
);
_31
_31
// Insert Block User Button into the Flex UI
_31
Flex.MessageBubble.Content.add(<BlockUserButton key="block-user" />, {
_31
if: (props) => !props.message.isFromMe
_31
});


Rate this page: