Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum AppSetting {

export enum DefaultMessage {
DEFAULT_DialogflowServiceUnavailableMessage = 'Sorry, I\'m having trouble answering your question.',
DEFAULT_DialogflowRequestFailedMessage = 'Sorry, something went wrong.',
DEFAULT_DialogflowHandoverMessage = 'Transferring to an online agent',
DEFAULT_DialogflowCloseChatMessage = 'Closing the chat, Goodbye',
}
Expand Down
57 changes: 55 additions & 2 deletions docs/QuickReplies.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,59 @@
| `text` | String | Title of the quick replies action. | Required | Any | ``` "text": "Start Chat" ``` |
| `actionId` | String | Id of the quick replies action. | Optional | Any | ``` "actionId": "sflaia-start-chat" ``` |
| `buttonStyle` | String | Button style of your quick replies action. Use `danger` to render a red colour action and `primary` for an action that matches your Livechat Bar colour. | Optional | `danger` or `primary` | ``` "buttonStyle": "primary" ``` |
### Example

![Quick Replies Example](https://user-images.githubusercontent.com/41849970/91997140-62939c00-ed57-11ea-8b27-82e650a502f0.png)
## Pre-Programmed Buttons

These buttons perform a specific action in the app. You can add them by simply pasting the following block in your Quick Replies payload. **Note**: You can change the `text` and `buttonStyle` parameters as per your requirements, but only use the provided `actionId` for the button you want to add.

### Handover Button

- On clicking this button, the visitor will be handed over to another departement. You can set the target department in the app setting called **Target Department for Handover** or add a `departmentName` param in your payload. On failing to provide a department name in either way, will send a request failure message back to the visitor, when visitor clicks the button.

- Add the following block in your Quick Replies payload, with **actionId** set as `df_perform_handover`, to include this button in your response:

- Parameters:

| Param Name | Dependency | Param Type | Acceptable Value |
|:----------------:|:------------:|:----------:|:-----------------------------------:|
| `actionId` | **Required** | String | `df_perform_handover` |
| `text` | **Required** | String | **Any** |
Comment thread
PrajvalRaval marked this conversation as resolved.
| `buttonStyle` | **Optional** | String | `primary` or `danger` |
| `departmentName` | **Optional** | String | **Any Omnichannel department name** |

- Example Structure:

```
{
"text": "Perform Handover",
"buttonStyle": "primary",
"actionId": "df_perform_handover",
"departmentName": "sales"
}
```

### Close Chat Button

- When visitor clicks this button, the chat session will be closed. Add the following block in your Quick Replies payload, with **actionId** set as `df_close_chat`, to include this button in your response:

- Parameters:

| Param Name | Dependency | Param Type | Acceptable Value |
|:-------------:|:------------:|:----------:|:---------------------:|
| `actionId` | **Required** | String | `df_close_chat` |
| `text` | **Required** | String | **Any** |
Comment thread
PrajvalRaval marked this conversation as resolved.
| `buttonStyle` | **Optional** | String | `primary` or `danger` |

- Example Structure:

```
{
"text": "Close Chat",
"buttonStyle": "danger",
"actionId": "df_close_chat"
}
```

## Example

![Pre-Programmed Example Payload](https://user-images.githubusercontent.com/41849970/92283593-d5e70a80-ef1d-11ea-8860-e91a4980515f.png)
4 changes: 4 additions & 0 deletions enum/ActionIds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum ActionIds {
PERFORM_HANDOVER = 'df_perform_handover',
CLOSE_CHAT = 'df_close_chat',
}
3 changes: 3 additions & 0 deletions enum/Dialogflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export interface IDialogflowQuickReplyOptions {
text: string;
actionId?: string;
buttonStyle?: ButtonStyle;
data?: {
[prop: string]: any;
};
}

export interface IDialogflowAccessToken {
Expand Down
28 changes: 23 additions & 5 deletions handler/ExecuteLivechatBlockActionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { ILivechatRoom } from '@rocket.chat/apps-engine/definition/livechat';
import { IUIKitResponse, UIKitLivechatBlockInteractionContext } from '@rocket.chat/apps-engine/definition/uikit';
import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer';
import { IUser } from '@rocket.chat/apps-engine/definition/users';
import { AppSetting } from '../config/Settings';
import { createLivechatMessage, deleteAllActionBlocks } from '../lib/Message';
import { AppSetting, DefaultMessage } from '../config/Settings';
import { ActionIds } from '../enum/ActionIds';
import { createLivechatMessage, createMessage, deleteAllActionBlocks } from '../lib/Message';
import { closeChat, performHandover } from '../lib/Room';
import { getAppSettingValue } from '../lib/Settings';

export class ExecuteLivechatBlockActionHandler {
Expand All @@ -19,8 +21,7 @@ export class ExecuteLivechatBlockActionHandler {
public async run(): Promise<IUIKitResponse> {
try {
const interactionData = this.context.getInteractionData();

const { visitor, room, container: { id, type }, value } = interactionData;
const { visitor, room, container: { id, type }, value, actionId } = interactionData;

if (type !== UIKitIncomingInteractionContainerType.MESSAGE) {
return this.context.getInteractionResponder().successResponse();
Expand All @@ -35,7 +36,24 @@ export class ExecuteLivechatBlockActionHandler {

const appUser = await this.read.getUserReader().getAppUser(this.app.getID()) as IUser;

await createLivechatMessage(rid, this.read, this.modify, { text: value }, visitor);
switch (actionId) {
case ActionIds.PERFORM_HANDOVER:
Comment thread
PrajvalRaval marked this conversation as resolved.
const targetDepartment: string = value || await getAppSettingValue(this.read, AppSetting.FallbackTargetDepartment);
if (!targetDepartment) {
await createMessage(rid, this.read, this.modify, { text: DefaultMessage.DEFAULT_DialogflowRequestFailedMessage });
break;
Comment thread
PrajvalRaval marked this conversation as resolved.
}
await performHandover(this.modify, this.read, rid, visitor.token, targetDepartment);
break;

case ActionIds.CLOSE_CHAT:
await closeChat(this.modify, this.read, rid);
break;

default:
await createLivechatMessage(rid, this.read, this.modify, { text: value }, visitor);
break;
}

const { value: hideQuickRepliesSetting } = await this.read.getEnvironmentReader().getSettings().getById(AppSetting.DialogflowHideQuickReplies);
if (hideQuickRepliesSetting) {
Expand Down
29 changes: 19 additions & 10 deletions lib/Message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IVisitor } from '@rocket.chat/apps-engine/definition/livechat';
import { BlockElementType, BlockType, IActionsBlock, IButtonElement, TextObjectType } from '@rocket.chat/apps-engine/definition/uikit';
import { IUser } from '@rocket.chat/apps-engine/definition/users';
import { AppSetting } from '../config/Settings';
import { ActionIds } from '../enum/ActionIds';
import { IDialogflowMessage, IDialogflowQuickReplies, IDialogflowQuickReplyOptions } from '../enum/Dialogflow';
import { Logs } from '../enum/Logs';
import { uuid } from './Helper';
Expand All @@ -14,16 +15,24 @@ export const createDialogflowMessage = async (rid: string, read: IRead, modify:
for (const message of messages) {
const { text, options } = message as IDialogflowQuickReplies;
if (text && options) {
const elements: Array<IButtonElement> = options.map((payload: IDialogflowQuickReplyOptions) => ({
type: BlockElementType.BUTTON,
text: {
type: TextObjectType.PLAINTEXT,
text: payload.text,
},
value: payload.text,
actionId: payload.actionId || uuid(),
...payload.buttonStyle && { style: payload.buttonStyle },
} as IButtonElement));
const elements: Array<IButtonElement> = options.map((payload: IDialogflowQuickReplyOptions) => {
const buttonElement: IButtonElement = {
type: BlockElementType.BUTTON,
actionId: payload.actionId || uuid(),
text: {
text: payload.text,
type: TextObjectType.PLAINTEXT,
},
value: payload.text,
Comment thread
PrajvalRaval marked this conversation as resolved.
...payload.buttonStyle && { style: payload.buttonStyle },
};

if (payload.actionId && payload.actionId === ActionIds.PERFORM_HANDOVER) {
buttonElement.value = payload.data && payload.data.departmentName ? payload.data.departmentName : undefined;
}

return buttonElement;
});

const actionsBlock: IActionsBlock = { type: BlockType.ACTIONS, elements };

Expand Down