Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
737dba5
Add Lex support to multiple languages
murilovmachado Jun 26, 2023
789fb1d
Fix tests 1
murilovmachado Jun 28, 2023
35ca6b9
Merge remote-tracking branch 'origin/master' into murilo_lex_2
murilovmachado Jun 28, 2023
a4a4832
Fix tests
murilovmachado Jul 3, 2023
25e0daf
add space
murilovmachado Jul 3, 2023
25e66c4
test 01
murilovmachado Jul 3, 2023
d5b865d
test 02
murilovmachado Jul 3, 2023
b40fdbf
test 03
murilovmachado Jul 3, 2023
809fc92
Save botName on channel attributes
murilovmachado Jul 3, 2023
4a62d09
Fix
murilovmachado Jul 3, 2023
7ec20f9
save controlTaskSid
murilovmachado Jul 3, 2023
fd3fc74
fix lint
murilovmachado Jul 3, 2023
56afffe
move control task to canceled
murilovmachado Jul 3, 2023
d9b8c94
Undo latest changes
murilovmachado Jul 3, 2023
4e0ff3b
language default to en-US
murilovmachado Jul 4, 2023
e0a22c0
Add some fixes from Gian's PR
murilovmachado Jul 4, 2023
5db22dc
fix language suffix
murilovmachado Jul 4, 2023
230c474
comment failing test
murilovmachado Jul 4, 2023
97e6f40
cleanup survey task
murilovmachado Jul 4, 2023
e356f12
Remove duplicated messages from bot
murilovmachado Jul 4, 2023
22e75be
Undo canceling survey task
murilovmachado Jul 4, 2023
070818b
Fix tests
murilovmachado Jul 4, 2023
2613c49
remove test step
murilovmachado Jul 4, 2023
268b5ea
remove comment
murilovmachado Jul 5, 2023
252da21
Merge remote-tracking branch 'origin/master' into murilo_lex_2
murilovmachado Jul 5, 2023
6a96acb
Merge remote-tracking branch 'origin/gian_CHI-175-lex' into murilo_lex_2
murilovmachado Jul 5, 2023
b5be8fd
remove comment
murilovmachado Jul 5, 2023
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
16 changes: 12 additions & 4 deletions .github/actions/main-action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ inputs:
s3-bucket:
description: 'S3 bucket name where Aselo documents are stored'
required: true
helpline-name:
description: 'The identifier in the format "<short helpline>-<short env>" used for this helpline'
helpline-code:
description: 'The short (usually 2 character) upper case code used to identify the helpline internally, e.g. ZA, IN, BR.'
required: true
environment-code:
description: 'The short upper case code used to identify the environment internally, e.g. STG, PROD, DEV'
required: true
send-slack-message:
description: 'Specifies if should send a Slack message at the end of successful run. Defaults to true'
Expand All @@ -81,6 +84,9 @@ inputs:
runs:
using: 'composite'
steps:
- name: Set helpline-name
run: echo "helpline-name=${{ inputs.helpline-code }}_${{ inputs.environment-code }}" >> $GITHUB_ENV
shell: bash
# Set any env vars needed from Parameter Store here
# Slack env
- name: Set GITHUB_ACTIONS_SLACK_BOT_TOKEN
Expand All @@ -100,6 +106,8 @@ runs:
- name: Fill .env
run: |
cat <<EOT >> .env
HELPLINE_CODE=${{ inputs.helpline-code }}
ENVIRONMENT_CODE=${{ inputs.environment-code }}
TWILIO_WORKSPACE_SID=${{ inputs.workspace-sid }}
TWILIO_CHAT_TRANSFER_WORKFLOW_SID=${{ inputs.transfer-workflow-sid }}
SYNC_SERVICE_API_KEY=${{ inputs.sync-service-api-key }}
Expand Down Expand Up @@ -131,7 +139,7 @@ runs:
- name: Execute custom action (if any)
uses: ./.github/actions/custom-actions
with:
helpline-name: ${{ inputs.helpline-name }}
helpline-name: ${{ env.helpline-name }}
account-sid: ${{ inputs.account-sid }}
# Install dependencies for the twilio functions
- name: Install dependencies for the twilio functions
Expand Down Expand Up @@ -170,6 +178,6 @@ runs:
if: ${{ inputs.send-slack-message != 'false' }}
with:
channel-id: ${{ env.ASELO_DEPLOYS_CHANNEL_ID }}
slack-message: "`[Serverless]` Deployment to `${{ inputs.helpline-name }}` from ${{ github.ref_type }} `${{ github.ref_name }}` requested by `${{ github.triggering_actor }}` completed using workflow '${{ github.workflow }}' with SHA ${{ github.sha }} :rocket:."
slack-message: "`[Serverless]` Deployment to `${{ env.helpline-name }}` from ${{ github.ref_type }} `${{ github.ref_name }}` requested by `${{ github.triggering_actor }}` completed using workflow '${{ github.workflow }}' with SHA ${{ github.sha }} :rocket:."
env:
SLACK_BOT_TOKEN: ${{ env.GITHUB_ACTIONS_SLACK_BOT_TOKEN }}
3 changes: 2 additions & 1 deletion .github/workflows/custom_helpline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ jobs:
aselo-app-secret-key: $ASELO_APP_SECRET_KEY
aws-region: $HELPLINE_AWS_REGION
s3-bucket: $S3_BUCKET
helpline-name: ${{inputs.helpline_code}}_${{inputs.environment_code}}
helpline-code: ${{inputs.helpline_code}}
environment-code: ${{inputs.environment_code}}
# Set 'false' if the target environment is production OR the force_enable_operating_hours override option is checked - otherwise 'true'
disable-operating-hours: ${{ (inputs.force_enable_operating_hours || inputs.environment_code == 'PROD') && 'false' || 'true' }}
send-slack-message: ${{ inputs.send-slack-message }}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ For help on twilio-run commands run:
| `S3_ENDPOINT` | _local transcripts only_ http://localhost:4566 |
| `ASELO_APP_ACCESS_KEY` | AWS_ACCESS_KEY_ID with access to s3 bucket (can be any string for localstack) |
| `ASELO_APP_SECRET_KEY` | AWS_SECRET_ACCESS_KEY for ASELO_APP_ACCESS_KEY (can be any string for localstack |
| `HELPLINE_CODE` | The short (usually 2 character) upper case code used to identify the helpline internally, e.g. ZA, IN, BR. |
| `ENVIRONMENT_CODE` | The short upper case code used to identify the environment internally, e.g. STG, PROD, DEV |

## Deployment

Expand Down
18 changes: 12 additions & 6 deletions functions/captureChannelWithBot.protected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ import {
error500,
success,
} from '@tech-matters/serverless-helpers';
import { LexClient } from './helpers/lexClient.private';
import { LexClient, BotType } from './helpers/lexClient.private';

type EnvVars = {
HELPLINE_CODE: string;
ENVIRONMENT_CODE: string;
CHAT_SERVICE_SID: string;
ASELO_APP_ACCESS_KEY: string;
ASELO_APP_SECRET_KEY: string;
Expand All @@ -42,7 +44,8 @@ export type Body = {
message: string; // (in Studio Flow, trigger.message.Body) The triggering message
fromServiceUser: string; // (in Studio Flow, trigger.message.From) The service user unique name
studioFlowSid: string; // (in Studio Flow, flow.flow_sid) The Studio Flow sid. Needed to trigger an API type execution once the channel is released.
botName: string;
language: string;
type: BotType;
};

export const handler = async (
Expand All @@ -55,7 +58,7 @@ export const handler = async (
const resolve = bindResolve(callback)(response);

try {
const { channelSid, message, fromServiceUser, studioFlowSid, botName } = event;
const { channelSid, message, fromServiceUser, studioFlowSid, language, type } = event;

if (!channelSid) {
resolve(error400('channelSid'));
Expand All @@ -73,8 +76,8 @@ export const handler = async (
resolve(error400('studioFlowSid'));
return;
}
if (!botName) {
resolve(error400('botName'));
if (!type) {
resolve(error400('type'));
return;
}

Expand All @@ -101,6 +104,10 @@ export const handler = async (
}),
);

const { ENVIRONMENT_CODE, HELPLINE_CODE } = context;
const languageSuffix = (language || 'en-US').replace('-', '_');
const botName = `${ENVIRONMENT_CODE}_${HELPLINE_CODE}_${type}_${languageSuffix}`;

const chatbotCallbackWebhook = await channel.webhooks().create({
type: 'webhook',
configuration: {
Expand All @@ -126,7 +133,6 @@ export const handler = async (

const updatedChannelAttributes = JSON.parse(updated.attributes);

// Cleanup task for captured channel by the bot
await context
.getTwilioClient()
.taskrouter.workspaces(context.TWILIO_WORKSPACE_SID)
Expand Down
9 changes: 6 additions & 3 deletions functions/helpers/lexClient.private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type AWSCredentials = {
AWS_REGION: string;
};

export type BotType = 'pre_survey' | 'post_survey';

export const postText = async (
credentials: AWSCredentials,
{
Expand All @@ -35,12 +37,13 @@ export const postText = async (
userId: string;
},
) => {
const { ASELO_APP_ACCESS_KEY, ASELO_APP_SECRET_KEY, AWS_REGION } = credentials;
AWS.config.update({
credentials: {
accessKeyId: credentials.ASELO_APP_ACCESS_KEY,
secretAccessKey: credentials.ASELO_APP_SECRET_KEY,
accessKeyId: ASELO_APP_ACCESS_KEY,
secretAccessKey: ASELO_APP_SECRET_KEY,
},
region: credentials.AWS_REGION,
region: AWS_REGION,
});

const Lex = new AWS.LexRuntime();
Expand Down
41 changes: 23 additions & 18 deletions functions/webhooks/chatbotCallback.protected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,27 +88,20 @@ export const handler = async (
userId: channel.sid,
});

await channel.messages().create({
body: lexResponse.message,
from: 'Bot',
xTwilioWebhookEnabled: 'true',
});

// If the session ended, we should unlock the channel to continue the Studio Flow
if (lexClient.isEndOfDialog(lexResponse.dialogState)) {
const releasedChannelAttributes = {
...omit(channelAttributes, 'channelCapturedByBot'),
memory: lexResponse.slots,
preSurveyComplete: true,
};

// TODO: This is now only assuming pre-survey bot. We should have a way to specify what's the next step after the bot execution is ended
const nextAction = client.studio.v2
.flows(channelAttributes.channelCapturedByBot.studioFlowSid)
.executions.create({
from: ChannelSid,
to: ChannelSid,
parameters: {
ChannelAttributes: releasedChannelAttributes,
const nextAction = () =>
channel.webhooks().create({
type: 'studio',
configuration: {
flowSid: channelAttributes.channelCapturedByBot.studioFlowSid,
},
});

Expand All @@ -124,22 +117,34 @@ export const handler = async (
attributes: JSON.stringify(releasedChannelAttributes),
}),
// Move control task to complete state
client.taskrouter.v1
.workspaces(context.TWILIO_WORKSPACE_SID)
.tasks(channelAttributes.controlTaskSid)
.update({ assignmentStatus: 'completed' }),
(async () => {
try {
await client.taskrouter.v1
.workspaces(context.TWILIO_WORKSPACE_SID)
.tasks(channelAttributes.controlTaskSid)
.update({ assignmentStatus: 'completed' });
} catch (err) {
console.log(err);
}
})(),
// Remove this webhook from the channel
channel
.webhooks()
.get(channelAttributes.channelCapturedByBot.chatbotCallbackWebhookSid)
.remove(),
// Trigger the next step once the channel is released
nextAction,
nextAction(),
]);

console.log('Channel unblocked and bot session deleted');
}

await channel.messages().create({
body: lexResponse.message,
from: 'Bot',
xTwilioWebhookEnabled: 'true',
});

resolve(success('All messages sent :)'));
return;
}
Expand Down
8 changes: 6 additions & 2 deletions tests/captureChannelWithBot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,17 @@ const mockContext = {
AWS_REGION: 'us-east-1',
TWILIO_WORKSPACE_SID: 'WE23xxx0orre',
SURVEY_WORKFLOW_SID: 'AZexxx903esd',
HELPLINE_CODE: 'AS',
ENVIRONMENT_CODE: 'DEV',
};

const mockEvent: Body = {
channelSid: 'SID123xxx09sa',
message: 'Message sent',
fromServiceUser: 'Test User',
studioFlowSid: 'FL0123xxdew',
botName: 'C6HUSTIFBR',
language: 'en_US',
type: 'pre_survey',
};

const mockCallback = jest.fn();
Expand Down Expand Up @@ -139,7 +142,8 @@ describe('captureChannelWithBot', () => {
message: 'Message sent',
fromServiceUser: 'Test User',
studioFlowSid: 'FL0123xxdew',
botName: 'C6HUSTIFBR',
language: 'en_US',
type: 'pre_survey',
};
await captureChannelWithBot(mockContext, event, mockCallback);

Expand Down
1 change: 1 addition & 0 deletions tests/webhooks/chatbotCallback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const context = {
get: jest.fn().mockReturnValue({
remove: jest.fn().mockReturnValue({}),
}),
create: jest.fn(),
}),
}),
messages: jest.fn().mockReturnValue({
Expand Down