# Resources: lambda, iam role for lambda, s3 bucket for website, cdn, api gateway, iam role to log api gateway
AWSTemplateFormatVersion: "2010-09-09"

Description: >
  "Preseve context across the voice channel and the chat channel"

Parameters:
  connectServicelinkedRoleARN:
    Type: String
    Description: ARN of the Service linked role of your connect instance
  instanceId:
    Type: String
    Description: The instance id of the Amazon Connect instance.
    AllowedPattern: '\w{8}-\w{4}-\w{4}-\w{4}-\w{12}'
  EmailAddress:
    Type: String
    Description: Customer email address. Password will be sent to this email address.
    AllowedPattern: '[^@]+@[^@]+\.[^@]+'
  PhoneNumber:
    Type: String
    Description: Customer Phone number in E. 164 format.
  S3bucketBucketPrefix:
    Type: String
    Description: S3 bucket name prefix. Allowed small case, number and has to be between 5 to 20 character in length
    AllowedPattern: ^[a-z][a-z0-9-]{5,21}[a-z0-9]$
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Stack Configuration"
        Parameters:
          - instanceId
          - connectServicelinkedRoleARN
          - S3bucketBucketPrefix
      - Label:
          default: "Customer Configuration"
        Parameters:
          - EmailAddress
          - PhoneNumber
Resources:
  lexSnapshots:
    Type: 'AWS::DynamoDB::Table'
    Properties:
      AttributeDefinitions:
        - AttributeName: PhoneNumber
          AttributeType: S
      KeySchema:
        - AttributeName: PhoneNumber
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST
  iamRole4SaveResumeBot:
    Type: 'AWS::IAM::Role'
    DependsOn:
      - lexSnapshots
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]
      Policies:
        - PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: VisualEditor0
                Effect: Allow
                Action: 
                  - 'dynamodb:PutItem'
                  - 'dynamodb:DeleteItem'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: !GetAtt 
                  - lexSnapshots
                  - Arn
          PolicyName: dynamoDBCloudwatchLogs
  iamRole4fetchCustomersPausedInteraction:
    Type: 'AWS::IAM::Role'
    DependsOn:
      - lexSnapshots
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]
      Policies:
        - PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: VisualEditor0
                Effect: Allow
                Action:
                  - 'dynamodb:DeleteItem'
                  - 'dynamodb:GetItem'
                  - 'logs:CreateLogStream'
                  - 'logs:PutLogEvents'
                Resource: !GetAtt 
                  - lexSnapshots
                  - Arn
          PolicyName: dynamoDBCloudwatchLogs
  BotFullfilmentLambda: 
    Type: AWS::Lambda::Function
    DependsOn:
      - iamRole4SaveResumeBot
    Properties:
      Runtime: nodejs22.x
      Code:
        ZipFile: |
          const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
          const { PutCommand, DynamoDBDocumentClient, DeleteCommand } = require("@aws-sdk/lib-dynamodb");


          exports.handler = async (event) => {
              var eventIntent = event.sessionState.intent.name;
              var eventSlots = event.sessionState.intent.slots;
              var sessionAttributes = event.sessionState.sessionAttributes;
              if (event.invocationSource == "DialogCodeHook") {
                  //save snapshot in sessionAttribute
                  sessionAttributes.snapshot = JSON.stringify(event.proposedNextState);
                  if (sessionAttributes.snapshot != "") {
                      await deleteSnapshot(event.sessionState.sessionAttributes.PhoneNumber);
                  }
              }
              if (event.invocationSource == "FulfillmentCodeHook" && event.sessionState.intent.name == "LackOfInfo") {
                  //save snapshot to contactId
                  await saveSnapshot(event.sessionState.sessionAttributes.PhoneNumber, event.sessionState.sessionAttributes.Channel, event.sessionState.sessionAttributes.contactId, event.sessionState.sessionAttributes.snapshot);
              }
              if (event.invocationSource == "FulfillmentCodeHook" && event.sessionState.intent.name == "ResumePreviousIntent") {
                  //resume from snapshot
                  var jSnapshot = JSON.parse(event.sessionState.sessionAttributes.snapshot);
                  sessionAttributes = event.sessionState.sessionAttributes;
                  eventIntent = jSnapshot.intent.name;
                  eventSlots = jSnapshot.intent.slots;
                  var lexResponse = elicitSlot(sessionAttributes,eventIntent,eventSlots,jSnapshot.dialogAction.slotToElicit)
              return lexResponse;
              }
              var lexResponse = delegate(sessionAttributes,eventIntent,eventSlots)
              return lexResponse;
          };
          //----------------- Helpers to Get & Put from dynamo DB ------------------------------
          const client = new DynamoDBClient({});
          const docClient = DynamoDBDocumentClient.from(client);

          async function saveSnapshot (PhoneNumber,Channel, InitialContactId,snapshot) {
              var params = {
                TableName : process.env.DDB_TABLE_NAME,
                Item: {
                  "PhoneNumber": PhoneNumber,
                  "Channel": Channel,
                  "InitialContactId": InitialContactId,
                  "snapshot": snapshot,
                  "intent": JSON.parse(snapshot).intent.name,
                  "status": "paused"
                }
              };
              const command = new PutCommand(params);
              try {
                const response = await docClient.send(command);
                console.log("save the snapshot in dynamoDb");
              } catch (error) {
                console.error("error in save the snapshot in dynamoDb,", error);
              }
          }

          async function deleteSnapshot (PhoneNumber) {
              var params = {
                TableName : process.env.DDB_TABLE_NAME,
                Key: {
                  PhoneNumber: PhoneNumber 
                }
              };
              try {
                const command = new DeleteCommand(params);
                const response = await docClient.send(command);
              } catch (error) {
                console.error("error in deleting the snapshot in dynamoDb,", error);
              }
              console.log("deleted the snapshot in dynamoDb");
          }

          // --------------- Helpers that build Lex responses ------------------------------
          // -------------------------------------------------------------------------------
          // -------------------------------------------------------------------------------
              
          function elicitSlot(sessionAttributes, IntentName, slots, slotToElicit) {
            //console.log("DEBUG>>elicitSlot sessionAttributes:" + JSON.stringify(sessionAttributes, null, 2) + ", intentName:" + JSON.stringify(IntentName, null, 2) + ", Slot:" + JSON.stringify(slots, null, 2) + ", slotToElicit:" + JSON.stringify(slotToElicit, null, 2));
              
              return {
                  'sessionState': {
                      'sessionAttributes': sessionAttributes,
                      "dialogAction": {
                      "slotToElicit": slotToElicit,
                      "type": "ElicitSlot"
                      },
                      intent: {
                      "name":IntentName,
                      "slots":slots
                      },
              }
                  
              };
          }

          function close(sessionAttributes, fulfillmentState, IntentName, message) {
              
              //console.log("DEBUG>>close sessionAttributes:" + JSON.stringify(sessionAttributes, null, 2) + ", fulfillmentState:" + JSON.stringify(fulfillmentState, null, 2) + ", message:" + JSON.stringify(message, null, 2));
              return {
                  messages: [ 
                      message
                      ],
                  'sessionState': {
                      'dialogAction': {
                          'type': 'Close'
                      },
                      'sessionAttributes': sessionAttributes,
                      intent: {
                      'state': fulfillmentState,
                      'name': IntentName, 
                  }   
                              }
                };
          }

          function delegate (sessionAttributes, IntentName, slots) {
              //console.log("DEBUG>>delegate sessionAttributes:" + JSON.stringify(sessionAttributes, null, 2) + ", slots:" + JSON.stringify(slots, null, 2) + " ,IntentName:" + IntentName);
                  return {            
                  'sessionState': {
                      'sessionAttributes': sessionAttributes,
                      "dialogAction": {
                      "type": "Delegate"
                      },
                      intent: {
                          "name": IntentName,
                          "slots": slots    
                  }
              }
                  
              };
          }
      Handler: index.handler
      MemorySize: 512
      Timeout: 10
      Environment:
        Variables:
          DDB_TABLE_NAME: !Ref lexSnapshots
          REGION: {Ref: "AWS::Region"}
      Role: !GetAtt iamRole4SaveResumeBot.Arn
  fetchCustomersPausedInteraction: 
    Type: AWS::Lambda::Function
    DependsOn:
      - iamRole4fetchCustomersPausedInteraction
    Properties:
      Runtime: nodejs22.x
      Code:
        ZipFile: | 
          const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
          const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb");

          exports.handler = async (event) => {
              console.log("event %j", event);
              var params = {
                TableName : process.env.DDB_TABLE_NAME,
                Key: {
                  "PhoneNumber": event.Details.Parameters.PhoneNumber
                }
              };
              const client = new DynamoDBClient({});
              const docClient = DynamoDBDocumentClient.from(client);
              const command = new GetCommand(params);
              var response = "";
              try {
                const data = await docClient.send(command);
                console.log(data);
                console.log("fetched the snapshot in dynamoDb:",data);
                if (data.Item != null && data.Item.status == "paused") {
                  data.Item.botPaused = "yes";
                } else {
                  data["Item"] = {};
                  data.Item.botPaused = "no";
                }
                response = data.Item;
              } catch (error) {
                console.error("error in fetching the snapshot in dynamoDb,", error);
                response = {
                    "botPaused": "error"
                };
              }
              return response;
              
          };
      Handler: index.handler
      MemorySize: 512
      Timeout: 10
      Environment:
        Variables:
          DDB_TABLE_NAME: !Ref lexSnapshots
          REGION: {Ref: "AWS::Region"}
      Role: !GetAtt iamRole4fetchCustomersPausedInteraction.Arn
  
  CognitoUserPoolUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UsernameAttributes:
      - email
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: true
        UnusedAccountValidityDays: 7
        InviteMessageTemplate:
          EmailMessage: Your username is {username} and temporary password is {####}      
      Schema:
      - AttributeDataType: String
        Name: email
        Required: true
  CognitoUserPoolUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      UserPoolId: !Ref CognitoUserPoolUserPool
      GenerateSecret: false
  CognitoUser:
    Type: AWS::Cognito::UserPoolUser
    Properties: 
      DesiredDeliveryMediums: 
        - EMAIL
      ForceAliasCreation: true
      Username: !Ref EmailAddress
      UserPoolId: !Ref CognitoUserPoolUserPool
      ValidationData:
        -  
          Name: email
          Value : !Ref EmailAddress

  LambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]
  
  BotFullfilmentLambdaLexPermission: #DONE
    Type: AWS::Lambda::Permission
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt BotFullfilmentLambda.Arn
      Principal: "lexv2.amazonaws.com"
      SourceArn: !Sub "arn:aws:lex:${AWS::Region}:${AWS::AccountId}:bot-alias/${LexBot}/TSTALIASID"
  
  BotFullfilmentLambdaLexIntentPermission: #DONE
    Type: AWS::Lambda::Permission
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt BotFullfilmentLambda.Arn
      Principal: "lexv2.amazonaws.com"
      SourceArn: !Sub "arn:aws:lex:${AWS::Region}:${AWS::AccountId}:intent:/*"
  
  InitiateChatLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      Description: AWS Lambda Function to initiate the chat with the end user
      Handler: "index.lambda_handler"
      Role: !GetAtt InitiateChatLambdaExecutionRole.Arn
      Runtime: python3.9
      MemorySize: 256
      Timeout: 30
      Environment:
        Variables:
          INSTANCE_ID: !Ref instanceId
          CONTACT_FLOW_ID: !GetAtt HelperLambdaExecution.ContactFlowId
          CF_DISTRIBUTION: !GetAtt CloudFrontDistribution.DomainName
          PhoneNumber: !Ref PhoneNumber
      Code:
        ZipFile: | 
          import json
          import boto3
          import os

          connect_client = boto3.client('connect')

          def lambda_handler(event, context):
              
              print(event['body'])
              body = json.loads(event['body'])

              
              userId = body['Username']
              displayName = body['Attributes']['customerName']
              contactFlowId = body['ContactFlowId']
              instanceId = body['InstanceId']
              
              result = {};
              result['username'] = userId;
              result['instanceId'] = instanceId;
              
              start_chat_contact_response = connect_client.start_chat_contact(
                  InstanceId=instanceId,
                  ContactFlowId=contactFlowId,
                  Attributes={
                      'customerName': displayName,
                      "username": displayName,
                      "PhoneNumber" : os.environ['PhoneNumber']
                  },
                  ParticipantDetails={
                      'DisplayName': userId
                  }
                  
              )
              print(start_chat_contact_response)
              result['startChatResult'] = start_chat_contact_response;
              response = {
                  'statusCode': 200,
                  'headers': {
                      "Access-Control-Allow-Origin": "*",
                      'Content-Type': 'application/json',
                      'Access-Control-Allow-Credentials' : True,
                      'Access-Control-Allow-Headers':'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
                  },
                  'body': json.dumps({
                      'data': result
                  })
                  }
              
              print(response)
              return response
  InitiateChatLambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns:
        - Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: initiate-chat-execution-policy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "connect:StartChatContact"
                Resource:
                  Fn::Join:
                    - ""
                    - - "arn:aws:connect:"
                      - Ref: AWS::Region
                      - ":"
                      - Ref: AWS::AccountId
                      - :instance/
                      - Ref: instanceId
                      - /contact-flow/
                      - !GetAtt HelperLambdaExecution.ContactFlowId
  
  #### Website #####
  CreateWebsiteS3Bucket:
    Type: "AWS::S3::Bucket"
    DeletionPolicy : "Retain"
    Properties:
      BucketName: !Sub ${S3bucketBucketPrefix}-${AWS::AccountId}-${AWS::Region}
      AccessControl: LogDeliveryWrite
      LoggingConfiguration:
        DestinationBucketName: !Sub ${S3bucketBucketPrefix}-${AWS::AccountId}-${AWS::Region}
        LogFilePrefix: "logs/"
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      WebsiteConfiguration:
          IndexDocument: 'index.html'
          ErrorDocument: 'error.html'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      CorsConfiguration:
        CorsRules:
          - AllowedHeaders:
              - "*"
            AllowedMethods:
              - GET
              - POST
            AllowedOrigins:
              - "*" 
            MaxAge: 3000
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter

  s3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref CreateWebsiteS3Bucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: "AllowCloudFrontServicePrincipalReadOnly"
            Effect: "Allow"
            Principal:
              Service: "cloudfront.amazonaws.com"
            Action: "s3:GetObject"
            Resource: !Sub "arn:aws:s3:::${CreateWebsiteS3Bucket}/*"
            Condition:
              StringEquals:
                "AWS:SourceArn": !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}"

  CloudFrontDistributionAccessControl:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: !Sub "${AWS::StackName}-oac"
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn:
      - connectSetupHelperLambda06BFAE1B
    Properties:
      DistributionConfig:
        HttpVersion: http2
        Origins:
          - DomainName: !GetAtt CreateWebsiteS3Bucket.RegionalDomainName
            Id: !GetAtt CreateWebsiteS3Bucket.RegionalDomainName
            S3OriginConfig:
              OriginAccessIdentity: ""
            OriginAccessControlId: !Ref CloudFrontDistributionAccessControl
        Enabled: true
        Comment: CloudFront for Chat UI
        DefaultRootObject: index.html
        DefaultCacheBehavior:
          AllowedMethods:
            - DELETE
            - GET
            - HEAD
            - OPTIONS
            - PATCH
            - POST
            - PUT
          TargetOriginId: !GetAtt CreateWebsiteS3Bucket.RegionalDomainName
          ForwardedValues:
            QueryString: false
            Cookies:
              Forward: none
          ViewerProtocolPolicy: redirect-to-https
        PriceClass: "PriceClass_100"
  
  ApiGatewayRestAPI: #DONE
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: "initiateChatConnection"
      Description: "API to initiate chat with Amazon Connect"
  
  ApiGatewayRequestValidator:
    Type: AWS::ApiGateway::RequestValidator
    Properties: 
      RestApiId: !Ref ApiGatewayRestAPI
      ValidateRequestBody: true
      ValidateRequestParameters: true
  
  ApiGatewayRestAPIAuthorizer:
    Type: AWS::ApiGateway::Authorizer
    Properties: 
      RestApiId: !Ref ApiGatewayRestAPI
      Name: COGNITO_USER_POOLS
      Type: COGNITO_USER_POOLS
      ProviderARNs:
        - !GetAtt CognitoUserPoolUserPool.Arn
      IdentitySource: method.request.header.Authorization

  LambdaApiGatewayInvokePermission: #DONE
    Type: AWS::Lambda::Permission
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !GetAtt InitiateChatLambda.Arn
      Principal: "apigateway.amazonaws.com"
      SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayRestAPI}/*"

  ApiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn:
      - ApiGatewayRootMethod
    Properties:
      RestApiId: !Ref ApiGatewayRestAPI
      StageName: "Prod"

  BotRuntimeRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lexv2.amazonaws.com
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: LexRuntimeRolePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "polly:SynthesizeSpeech"
                Resource: "*"
  
  LexBot:
    DependsOn: BotRuntimeRole
    Type: AWS::Lex::Bot
    
      
    Properties:
      Name: !Sub "${AWS::StackName}-Insure"
      RoleArn: !GetAtt BotRuntimeRole.Arn
      DataPrivacy:
        ChildDirected: false
      IdleSessionTTLInSeconds: 300
      Description: "Bot to demonstrate how to preserve contexts across channels"
      AutoBuildBotLocales: true
      TestBotAliasSettings:
        BotAliasLocaleSettings:
        - LocaleId: en_US
          BotAliasLocaleSetting:
            Enabled: true
            CodeHookSpecification: 
              LambdaCodeHook:
                CodeHookInterfaceVersion: "1.0"
                LambdaArn: !GetAtt BotFullfilmentLambda.Arn

      BotLocales:
        - LocaleId: "en_US"
          Description: "Bot to demonstrate how to preserve contexts across channels"
          NluConfidenceThreshold: 0.80
          VoiceSettings:
            VoiceId: "Ivy"
          SlotTypes:
            - Name: "YesNo"
              Description : "Yes or No Slot value"
              ValueSelectionSetting:
                ResolutionStrategy: "TOP_RESOLUTION"
              SlotTypeValues:
                - SampleValue: 
                    Value: "yes"
                  Synonyms:
                    - Value: "yes"
                    - Value: "ohh yes"
                    - Value: "that is right"
                    - Value: "it is"
                - SampleValue:
                    Value: "no"
                  Synonyms:
                    - Value: "no"
                    - Value: "ohh no"
                    - Value: "that is not right"
                    - Value: "it is not"
          Intents:
          - Name: "AgentSupport"
            Description: "Intent for agent request"
            SampleUtterances:
              - Utterance: "Speak to an agent"
              - Utterance: "talk to an agent"
              - Utterance: "speak to an insurance officer"
              - Utterance: "talk to an insurance officer"
          - Name: "AutoClaims"
            Description: "Intent for Auto Claims"
            DialogCodeHook: 
              Enabled: true
            FulfillmentCodeHook: 
              Enabled: true
            IntentClosingSetting: 
              IsActive: true
              ClosingResponse: 
                MessageGroupsList:
                  - Message:
                      PlainTextMessage:
                        Value: "We will send you an email to your registered email address with a link to upload the accident report. While we wait for your accident report. We assure you a seamless experience in claim process."
            SampleUtterances:
              - Utterance: "claim for automobile insurance policy"
              - Utterance: "submit an automobile claim"
              - Utterance: "file a claim"
              - Utterance: "submit a claim request"
            SlotPriorities:
              - Priority: 1
                SlotName: SSN
              - Priority: 2
                SlotName: PolicyNumber
              - Priority: 3
                SlotName: PolicyHolderVehicleDamaged
              - Priority: 4
                SlotName: PolicyHolderVehicleNumber
              - Priority: 5
                SlotName: ThirdPartyVehicleDamaged
              - Priority: 6
                SlotName: ClaimThirdPartyDamage  
              - Priority: 7
                SlotName: DateOfDamage
              - Priority: 8
                SlotName: AccidentReport
            Slots:
              - Name: "SSN"
                Description: "Last 4 digit of SSN"
                SlotTypeName: "AMAZON.Number"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "what is your last 4 digit of your SSN?"
                    MaxRetries: 3
                    AllowInterrupt: true
                  SampleUtterances:
                    - Utterance: "my SSN is {SSN}"
                    - Utterance: "last 4 digit SSN is {SSN}"
                    - Utterance: "it is {SSN}"
                    - Utterance: "it's {SSN}"
                  WaitAndContinueSpecification:
                    IsActive: true
                    ContinueResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Great. Let's continue, what is the last 4 digit of your SSN?"
                    StillWaitingResponse:
                      FrequencyInSeconds: 15
                      TimeoutInSeconds: 120
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Gentle reminder that we are waiting. You could say, let's continue, when you have your SSN."
                    WaitingResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "We could wait while you get hold of the last 4 digit of your SSN. You could say, let's continue, when you have your SSN."
                            
              - Name: "PolicyNumber"
                Description: "Policy Number"
                SlotTypeName: "AMAZON.AlphaNumeric"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "What is the policy number against which you would like to process the claim?"
                    MaxRetries: 3
                    AllowInterrupt: true
                  SampleUtterances:
                    - Utterance: "it is {PolicyNumber}"
                    - Utterance: "it's {PolicyNumber}"
                  WaitAndContinueSpecification:
                    IsActive: true
                    ContinueResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Great. Let's continue, what is your policy number?"
                    StillWaitingResponse:
                      FrequencyInSeconds: 15
                      TimeoutInSeconds: 120
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Gentle reminder that we are waiting. You could say, let's continue, when you have your policy number."
                    WaitingResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "We could wait while you get hold of the policy number. You could say, let's continue,when you have your policy number."
              
              - Name: "PolicyHolderVehicleDamaged"
                Description: "Is policy holder vehicle number damaged"
                SlotTypeName: "YesNo"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "Is the policy holder vehicle damaged?"
                    MaxRetries: 3
                    AllowInterrupt: true
                  
              - Name: "PolicyHolderVehicleNumber"
                Description: "Policy Holders Vehicle number"
                SlotTypeName: "AMAZON.AlphaNumeric"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "What is your vehicle number?"
                    MaxRetries: 3
                    AllowInterrupt: true
                  SampleUtterances:
                    - Utterance: "it is {PolicyHolderVehicleNumber}"
                    - Utterance: "it's {PolicyHolderVehicleNumber}"
                  WaitAndContinueSpecification:
                    IsActive: true
                    ContinueResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Great. Let's continue, what is your vehicle number?"
                    StillWaitingResponse:
                      FrequencyInSeconds: 15
                      TimeoutInSeconds: 120
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Gentle reminder that we are waiting. You could say, let's continue, when you have your vehicle number."
                    WaitingResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "We could wait while you get hold of the policy number. You could say, let's continue, when you have your vehicle number."        
              
              - Name: "ThirdPartyVehicleDamaged"
                Description: "is Third party vehicle damanged"
                SlotTypeName: "YesNo"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "Was there a damage to other party Vehicle?"
                    MaxRetries: 3
                    AllowInterrupt: true
                  SlotCaptureSetting:
                    CaptureConditional:
                      IsActive: true
                      DefaultBranch:
                        NextStep:
                          DialogAction:
                            SlotToElicit: "ClaimThirdPartyDamage"
                            Type: "ElicitSlot"
                      ConditionalBranches: 
                        - Condition: 
                            ExpressionString: "{ClaimThirdPartyDamage} = \"no\""
                          Name: "no3PVehicleDamage"
                          NextStep:
                            Intent:
                              Name: "AutoClaims"
                              Slots: 
                                - SlotName: ClaimThirdPartyDamage
                                  SlotValueOverride: 
                                    Value: 
                                      InterpretedValue: "no"
                            DialogAction:
                              SlotToElicit: "DateOfDamage"
                              Type: "ElicitSlot"
              
              - Name: "ClaimThirdPartyDamage"
                Description: "Do policy holder wants to claim thrid party damage coverage"
                SlotTypeName: "YesNo"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "Do you want to claim the third party damage?"
                    MaxRetries: 3
                    AllowInterrupt: true
                
              - Name: "DateOfDamage"
                Description: "Date of damage"
                SlotTypeName: "AMAZON.Date"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "on what date was the Vehicle damaged?"
                    MaxRetries: 3
                    AllowInterrupt: true
                  SampleUtterances:
                    - Utterance: "on {DateOfDamage}"
                    - Utterance: "it's was on {DateOfDamage}"
                  WaitAndContinueSpecification:
                    IsActive: true
                    ContinueResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Great. Let's continue, when was your vehicle damaged?"
                    StillWaitingResponse:
                      FrequencyInSeconds: 15
                      TimeoutInSeconds: 120
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "Gentle reminder that we are waiting. You could say, let's continue, when you know the date of vehicle damage."
                    WaitingResponse:
                      MessageGroupsList:
                        - Message:
                            PlainTextMessage:
                              Value: "We could wait while you get hold of the policy number. You could say, let's continue, when you know the date of vehicle damage."        
              
              - Name: "AccidentReport"
                Description: "Accident report avaiability"
                SlotTypeName: "YesNo"
                ValueElicitationSetting:
                  SlotConstraint: "Required"
                  PromptSpecification:
                    MessageGroupsList:
                      - Message:
                          PlainTextMessage:
                            Value: "Do you have an police accident report?"
                    MaxRetries: 3
                    AllowInterrupt: true
                  SlotCaptureSetting:
                    CaptureConditional:
                      IsActive: true
                      ConditionalBranches: 
                        - Condition: 
                            ExpressionString: "{AccidentReport} = \"no\""
                          Name: "hasNoReport"
                          NextStep:
                            DialogAction:
                              Type: "CloseIntent"
                          Response: 
                            MessageGroupsList:
                              - Message:
                                  PlainTextMessage:
                                    Value: "We would need a local police report on the damage or lost vehicle. We can process your claim once we receive the police report. While our intent is always to make claim process easier to our customer, it’s the policy guidelines to act based on the police report only."
                      DefaultBranch:
                        Response: 
                          MessageGroupsList:
                            - Message:
                                PlainTextMessage:
                                  Value: "We can process your claim once we receive the police report."
          - Name: "LackOfInfo"
            Description: "Pause the intent due to lack of information handy with the customer"
            FulfillmentCodeHook: 
              Enabled: true
            IntentClosingSetting: 
              IsActive: true
              ClosingResponse: 
                MessageGroupsList:
                  - Message:
                      PlainTextMessage:
                        Value: "No worries, we have preserved your information provided so far. You could contact us back on a call or over a chat and we could resume our interaction. While we wait to hear back from you."
            SampleUtterances:
              - Utterance: "I don't know"
              - Utterance: "don't have that information handy"
              - Utterance: "oops don't know"
              - Utterance: "I will have to get back with that information"
          - Name: "ResumePreviousIntent"
            Description: "Resuming the past paused interaction"
            InitialResponseSetting:
              InitialResponse:
                MessageGroupsList:
                  - Message:
                      PlainTextMessage: 
                        Value: "Okay, lets continue from where you left."
            FulfillmentCodeHook: 
              Enabled: true  
            SampleUtterances:
              - Utterance: "yes, I'm calling in for the reason"
              - Utterance: "yes, thats why I'm calling back"
              - Utterance: "yes, thats is right"
              - Utterance: "yes"
          - Name: "FallbackIntent"
            Description: "Default intent when no other intent matches"
            ParentIntentSignature: "AMAZON.FallbackIntent"
  
  ApiGatewayRootMethod: #DONE
    Type: AWS::ApiGateway::Method
    DependsOn: LambdaApiGatewayInvokePermission
    Properties:
      AuthorizationType: COGNITO_USER_POOLS
      AuthorizerId: !Ref ApiGatewayRestAPIAuthorizer
      HttpMethod: "POST"
      Integration:
        IntegrationHttpMethod: "POST"
        Type: "AWS_PROXY"
        Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${InitiateChatLambda.Arn}/invocations"
        PassthroughBehavior: WHEN_NO_MATCH
      ResourceId: !GetAtt ApiGatewayRestAPI.RootResourceId
      RestApiId: !Ref ApiGatewayRestAPI
      MethodResponses:
        - StatusCode: "200"
          ResponseParameters:
            method.response.header.Access-Control-Allow-Origin: true
          ResponseModels:
            application/json: Empty
        - StatusCode: "500"
          ResponseParameters:
            method.response.header.Access-Control-Allow-Origin: true
          ResponseModels:
            application/json: Empty

  ApiGatewayOptionsMethod: #DONE
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      ResourceId: !GetAtt ApiGatewayRestAPI.RootResourceId
      RestApiId: !Ref ApiGatewayRestAPI
      HttpMethod: OPTIONS
      Integration:
        Type: MOCK
        IntegrationResponses:
          - ResponseParameters:
              method.response.header.Access-Control-Allow-Headers: >-
                'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
              method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
            ResponseTemplates:
              application/json: ""
            StatusCode: "200"
        PassthroughBehavior: NEVER
        RequestTemplates:
          application/json: '{"statusCode": 200}'
      MethodResponses:
        - ResponseModels:
            application/json: Empty
          ResponseParameters:
            method.response.header.Access-Control-Allow-Headers: true
            method.response.header.Access-Control-Allow-Methods: true
            method.response.header.Access-Control-Allow-Origin: true
          StatusCode: "200"

  connectpolicy9F3C65F1:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action: connect:ListQueues
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
                  - /queue/*
          - Action: connect:CreateContactFlow
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
                  - /contact-flow/*
          - Action: connect:UpdateContactFlowName
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
                  - /contact-flow/*
          - Action: iam:PutRolePolicy
            Effect: Allow
            Resource:
              Ref: connectServicelinkedRoleARN
          - Action: iam:AttachRolePolicy
            Effect: Allow
            Resource:
              Ref: connectServicelinkedRoleARN
          - Action: connect:ListContactFlows
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
                  - /contact-flow/*
          - Action: connect:AssociateLambdaFunction
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
          - Action: connect:AssociateBot
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
          - Action: lex:CreateResourcePolicy
            Effect: Allow
            Resource:
              - Fn::Join:
                - ""
                - - "arn:aws:lex:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :bot/
                  - Ref: LexBot
              - Fn::Join:
                - ""
                - - "arn:aws:lex:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :bot-alias/
                  - Ref: LexBot
                  - "/TSTALIASID"
          - Action: lex:GetBot
            Effect: Allow
            Resource:
              Fn::Join:
              - ""
              - - "arn:aws:lex:"
                - Ref: AWS::Region
                - ":"
                - Ref: AWS::AccountId
                - :bot/
                - Ref: LexBot
          - Action: lex:BuildBotLocale
            Effect: Allow
            Resource:
              Fn::Join:
              - ""
              - - "arn:aws:lex:"
                - Ref: AWS::Region
                - ":"
                - Ref: AWS::AccountId
                - :bot/
                - Ref: LexBot
          - Action: lex:DescribeBotLocale
            Effect: Allow
            Resource:
              Fn::Join:
              - ""
              - - "arn:aws:lex:"
                - Ref: AWS::Region
                - ":"
                - Ref: AWS::AccountId
                - :bot/
                - Ref: LexBot
          - Action: lex:DescribeBotAlias
            Effect: Allow
            Resource:
              Fn::Join:
              - ""
              - - "arn:aws:lex:"
                - Ref: AWS::Region
                - ":"
                - Ref: AWS::AccountId
                - :bot-alias/
                - Ref: LexBot
                - "/TSTALIASID"
          - Action: connect:AssociateLambdaFunction
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
          - Action: lex:UpdateResourcePolicy
            Effect: Allow
            Resource:
              - Fn::Join:
                - ""
                - - "arn:aws:lex:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :bot/
                  - Ref: LexBot
              - Fn::Join:
                - ""
                - - "arn:aws:lex:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :bot-alias/
                  - Ref: LexBot
                  - "/TSTALIASID"
          - Action: connect:DisassociateLambdaFunction
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
          - Action: s3:PutObject
            Effect: Allow
            Resource: 
              Fn::Join:
                - ""
                - - !GetAtt CreateWebsiteS3Bucket.Arn
                  - "/*"
          - Action: lambda:AddPermission
            Effect: Allow
            Resource:
              Fn::GetAtt:
                - fetchCustomersPausedInteraction
                - Arn
          - Action: connect:ListContactFlows
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:connect:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/
                  - Ref: instanceId
        Version: "2012-10-17"
      PolicyName: connectpolicy9F3C65F1
      Roles:
        - Ref: connectSetupHelperLambdaServiceRole1BC05734
    Metadata:
      aws:cdk:path: CL-Streaming-API-Sample/connect-policy/Resource
  
  HelperLambdaExecution:
    Type: AWS::CloudFormation::CustomResource
    DependsOn: connectpolicy9F3C65F1
    Properties:
      ServiceToken: !GetAtt connectSetupHelperLambda06BFAE1B.Arn
      customAction: "lambda_handler" 

  connectSetupHelperLambdaServiceRole1BC05734:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: "2012-10-17"
      ManagedPolicyArns:
        - Fn::Join:
            - ""
            - - "arn:"
              - Ref: AWS::Partition
              - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
  connectSetupHelperLambda06BFAE1B:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: |
          import json
          import boto3
          import os
          import urllib.request
          import json
          import zipfile
          import random
          import time
          import string

          client = boto3.client('connect')
          SUCCESS = "SUCCESS"
          FAILED = "FAILED"
          s3_client = boto3.client('s3')
          lex_client = boto3.client('lexv2-models')

          S3_BUCKET_NAME = os.environ['S3_BUCKET_NAME']
          STATIC_SITE_ZIP = os.environ['STATIC_SITE_ZIP']


          def lambda_handler(event, context):

              responseData = {}

              print(event)

              if(event['RequestType'] == "Create"):
              
                  #Create contact flow
                  response = client.list_queues(InstanceId=os.environ['CONNECT_INSTANCE_ID'],QueueTypes=['STANDARD'])
                  basic_queue = list(filter(lambda x: (x['Name'] == 'BasicQueue'),response['QueueSummaryList']))
                  basic_queue_arn = basic_queue[0]['Arn']
                  
                  contact_flow_res = urllib.request.urlopen(urllib.request.Request(
                      url=os.environ['CONTACT_FLOW_TEMPLATE'],
                      headers={'Accept': 'application/json'},
                      method='GET'),
                  timeout=5)

                  #Import the Bot

                  build_bot_response = lex_client.build_bot_locale(
                      botId=os.environ['BOTID'],
                      botVersion='DRAFT',
                      localeId='en_US')
                  print(build_bot_response)

                  time.sleep(120)

                  lex_Response = lex_client.create_resource_policy_statement(
                      resourceArn=os.environ['LEX_ALIAS_ARN'],
                      statementId=generate_random_alphanumeric(10),
                      effect='Allow',
                      principal=[{'service': 'connect.amazonaws.com'}],
                      action=['lex:RecognizeText','lex:StartConversation'],
                      condition={
                          "StringEquals": {
                              "aws:SourceAccount": os.environ['ACCOUNT_ID']
                          },
                          "ArnEquals": {
                            "aws:SourceArn": "arn:aws:connect:" + os.environ['REGION'] +":" + os.environ['ACCOUNT_ID'] + ":instance/" + os.environ['CONNECT_INSTANCE_ID']
                          }
                      }
                  )
                  
                  associateBot = client.associate_bot(
                      InstanceId=os.environ['CONNECT_INSTANCE_ID'],
                      LexV2Bot={'AliasArn': os.environ['LEX_ALIAS_ARN']}
                  )

                  associateLambda = client.associate_lambda_function(
                      InstanceId=os.environ['CONNECT_INSTANCE_ID'],
                      FunctionArn=os.environ['LAMBDA_ARN']
                  )
                  
                  contact_flow = str(contact_flow_res.read().decode('utf-8')).replace("BASIC-QUEUE-ARN",basic_queue_arn).replace("LEX-BOT-ALIAS",os.environ['LEX_ALIAS_ARN']).replace("LAMBDA-ARN",os.environ['LAMBDA_ARN'])
                  
                  create_contact_flow_response = client.create_contact_flow(
                      InstanceId=str(os.environ['CONNECT_INSTANCE_ID']),
                      Name='PreserveContextFlow_' + generate_random_alphanumeric(6),
                      Type='CONTACT_FLOW',
                      Content=contact_flow
                  )
                  #Custom website - Updates
                  static_s3_zip = urllib.request.urlopen(urllib.request.Request(
                      url=STATIC_SITE_ZIP,
                      method='GET'),timeout=5)
                  
                  newFile = open('/tmp/myZip.zip','wb')
                  newFile.write(static_s3_zip.read())
                  print(newFile.name)
                  
                  print(os.getcwd())
                  os.chdir('/tmp')
                  print(os.getcwd())
                  
                  with zipfile.ZipFile('myZip.zip', 'r') as archive:
                      archive.extractall()
                      
                  path = os.getcwd()
                  
                  session = boto3.Session()
                  s3 = session.resource('s3')
                  bucket = s3.Bucket(S3_BUCKET_NAME)
              
                  for subdir, dirs, files in os.walk(path):
                      for file in files:
                          print(file)
                          if(file == 'index.html'):
                              full_path = os.path.join(subdir, file)
                              with open(full_path, 'r') as rfile:
                                  data = rfile.read()
                                  data = data.replace('CONNECT_INSTANCE_ID', os.environ['CONNECT_INSTANCE_ID'])
                                  data = data.replace('CONNECT_CONTACT_FLOW_ID',create_contact_flow_response["ContactFlowId"])
                                  data = data.replace('REGION',os.environ['REGION'])
                                  data = data.replace('API_ID',os.environ['API_ID'])
                                  with open(full_path, 'w') as wfile:
                                      wfile.write(data)
                          if(file == 'config.js'):
                              full_path = os.path.join(subdir, file)
                              with open(full_path, 'r') as rfile:
                                  data = rfile.read()
                                  data = data.replace('COGNITO_USER_POOL_ID', os.environ['COGNITO_USER_POOL_ID'])
                                  data = data.replace('COGNITO_CLIENT_ID',os.environ['COGNITO_CLIENT_ID'])
                                  with open(full_path, 'w') as wfile:
                                      wfile.write(data)
                          full_path = os.path.join(subdir, file)
                          with open(full_path, 'rb') as data:
                              if(file.split('.')[-1] == 'jpg'):
                                  contenttype = 'image/' + file.split('.')[-1]
                              else:
                                  contenttype = 'text/' + file.split('.')[-1]
                              bucket.put_object(Key=full_path[len(path)+1:], Body=data, ContentType=contenttype)
              
                  print ('Wrote files')

                  responseData["ContactFlowId"] = create_contact_flow_response["ContactFlowId"]

                  print(create_contact_flow_response)
                  
                  send(event, context, SUCCESS ,responseData, context.invoked_function_arn)

              if(event['RequestType'] == "Delete"):

                  #Time to clean up all things we have done

                  #Delete objects from static site
                  print('Delete')
                  

                  send(event, context, SUCCESS ,responseData, context.invoked_function_arn)
              
              return {
                  'statusCode': 200,
                  'body': json.dumps('Finished setting up the connect instance')
              }
          def generate_random_alphanumeric(length):
            """
            Generates a random alphanumeric string of a specified length.

            Args:
              length: The desired length of the alphanumeric string.

            Returns:
              A random alphanumeric string.
            """
            characters = string.ascii_letters + string.digits
            random_string = ''.join(random.choice(characters) for _ in range(length))
            return random_string
          
          def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False):
              responseUrl = event['ResponseURL']

              print(responseUrl)

              responseBody = {}
              responseBody['Status'] = responseStatus
              responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
              responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
              responseBody['StackId'] = event['StackId']
              responseBody['RequestId'] = event['RequestId']
              responseBody['LogicalResourceId'] = event['LogicalResourceId']
              responseBody['NoEcho'] = noEcho
              responseBody['Data'] = responseData

              json_responseBody = json.dumps(responseBody)

              print("Response body:\n" + json_responseBody)

              headers = {
                  'content-type' : '',
                  'content-length' : str(len(json_responseBody))
              }

              try:
                  arr = bytes(json_responseBody, 'utf-8')
                  response = urllib.request.Request(responseUrl,
                                          data=arr,
                                          headers=headers, method='PUT')
                  with urllib.request.urlopen(response) as f:
                      pass
                      
                
              except Exception as e:
                  print("send(..) failed executing requests.put(..): " + str(e))
      Role:
        Fn::GetAtt:
          - connectSetupHelperLambdaServiceRole1BC05734
          - Arn
      Environment:
        Variables:
          BOTID:
            Ref: LexBot
          ACCOUNT_ID: 
            Ref: AWS::AccountId
          REGION: 
            Ref: AWS::Region
          LEX_ALIAS_ARN:
            Fn::Join:
              - ""
              - - "arn:aws:lex:"
                - Ref: AWS::Region
                - ":"
                - Ref: AWS::AccountId
                - :bot-alias/
                - Ref: LexBot
                - "/TSTALIASID"
          COGNITO_USER_POOL_ID : !Ref CognitoUserPoolUserPool
          COGNITO_CLIENT_ID: !Ref CognitoUserPoolUserPoolClient
          LAMBDA_ARN : !GetAtt 'fetchCustomersPausedInteraction.Arn'
          STATIC_SITE_ZIP: https://aws-contact-center-blog.s3.us-west-2.amazonaws.com/preserve-interaction-context-amazon-connect/store-front-website.zip
          S3_BUCKET_NAME: !Sub ${S3bucketBucketPrefix}-${AWS::AccountId}-${AWS::Region}
          API_ID: !Ref ApiGatewayRestAPI
          CONNECT_INSTANCE_ID:
            Ref: instanceId
          CONTACT_FLOW_TEMPLATE: https://aws-contact-center-blog.s3.us-west-2.amazonaws.com/preserve-interaction-context-amazon-connect/AutoInsurance
          #CONTACT_FLOW_TEMPLATE: 
      Handler: index.lambda_handler
      MemorySize: 256
      Runtime: python3.9
      Timeout: 300
    DependsOn:
      - connectSetupHelperLambdaServiceRole1BC05734

Outputs:
  cloudFrontDistribution:
    Description: The domain name of the CloudFront Distribution to host the chat website
    Value: !GetAtt CloudFrontDistribution.DomainName
  