All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bot-connector-whatsapp-cloud.24.3.0.source-code.WhatsAppCloudBuilder.kt Maven / Gradle / Ivy

There is a newer version: 24.3.6
Show newest version
/*
 * Copyright (C) 2017/2021 e-voyageurs technologies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ai.tock.bot.connector.whatsapp.cloud

import ai.tock.bot.connector.ConnectorMessage
import ai.tock.bot.connector.ConnectorType
import ai.tock.bot.connector.whatsapp.cloud.model.common.TextContent
import ai.tock.bot.connector.whatsapp.cloud.model.send.QuickReply
import ai.tock.bot.connector.whatsapp.cloud.model.send.message.WhatsAppCloudBotMessage
import ai.tock.bot.connector.whatsapp.cloud.model.send.message.WhatsAppCloudBotRecipientType
import ai.tock.bot.connector.whatsapp.cloud.model.send.message.content.*
import ai.tock.bot.connector.whatsapp.cloud.model.send.message.content.WhatsAppCloudBotBody
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.Parameters
import ai.tock.bot.definition.StoryHandlerDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.engine.BotBus
import ai.tock.bot.engine.Bus
import ai.tock.bot.engine.I18nTranslator
import ai.tock.bot.engine.action.SendChoice
import mu.KotlinLogging
import java.util.*

private val logger = KotlinLogging.logger {}

internal const val WHATS_APP_CONNECTOR_TYPE_ID = "whatsapp_cloud"
private const val WHATS_APP_BUTTONS_TITLE_MAX_LENGTH = 50
private const val WHATS_APP_BUTTONS_ID_MAX_LENGTH = 256
private const val WHATS_APP_SECTION_TITLE_MAX_LENGTH = 24
private const val WHATS_APP_ROW_TITLE_MAX_LENGTH = 24
private const val WHATS_APP_ROW_ID_MAX_LENGTH = 200
private const val WHATS_APP_ROW_DESCRIPTION_MAX_LENGTH = 72
private const val WHATS_APP_MAX_ROWS = 10
private const val WHATS_APP_MAX_SECTIONS = 10

/**
 * The WhatsApp cloud connector type.
 * Les methodes présente ici sont utilisé pour que le chat bot répond a l'utilisateur
 */
val whatsAppCloudConnectorType = ConnectorType(WHATS_APP_CONNECTOR_TYPE_ID)

/**
 * Sends an WhatsApp message only if the [ConnectorType] of the current [BotBus] is [whatsAppCloudConnectorType].
 */
fun > T.sendToWhatsApp(
    messageProvider: T.() -> WhatsAppCloudBotMessage,
    delay: Long = defaultDelay(currentAnswerIndex)
): T {
    if (isCompatibleWith(whatsAppCloudConnectorType)) {
        withMessage(messageProvider(this))
        send(delay)
    }
    return this
}


/**
 * Adds a WhatsApp [ConnectorMessage] if the current connector is WhatsApp.
 * You need to call [BotBus.send] or [BotBus.end] later to send this message.
 */
fun > T.withWhatsAppCloud(messageProvider: () -> WhatsAppCloudConnectorMessage): T {
    return withMessage(whatsAppCloudConnectorType, messageProvider)
}

/**
 * Creates a [WhatsAppBotTextMessage].
 *
 * @param text the text sent
 * @param previewUrl is preview mode is used?
 */
fun BotBus.whatsAppText(
    text: CharSequence,
    previewUrl: Boolean = false
): WhatsAppCloudBotTextMessage =
    WhatsAppCloudBotTextMessage(
        messagingProduct = "whatsapp",
        text = TextContent(translate(text).toString()),
        recipientType = WhatsAppCloudBotRecipientType.individual,
        userId = userId.id,
    )

fun I18nTranslator.replyButtonMessage(
    text: CharSequence,
    vararg replies: QuickReply,

    ) : WhatsAppCloudBotInteractiveMessage = replyButtonMessage(text, replies.toList())

fun I18nTranslator.replyButtonMessage(
    text: CharSequence,
    replies: List,
) : WhatsAppCloudBotInteractiveMessage = WhatsAppCloudBotInteractiveMessage(
    messagingProduct = "whatsapp",
    recipientType = WhatsAppCloudBotRecipientType.individual,
    interactive = WhatsAppCloudBotInteractive(
        type = WhatsAppCloudBotInteractiveType.button,
        body = WhatsAppCloudBotBody(translate(text).toString()),
        action = WhatsAppCloudBotAction(
            buttons = replies.map {
                WhatsAppCloudBotActionButton(
                    reply = WhatsAppCloudBotActionButtonReply(
                        id = it.payload.checkLength(WHATS_APP_BUTTONS_ID_MAX_LENGTH),
                        title = translate(it.title).toString().checkLength(WHATS_APP_BUTTONS_TITLE_MAX_LENGTH),
                    )
                )
            }
        )
    )
)

fun I18nTranslator.urlButtonMessage(
    text: CharSequence?=null,
    textButton: String,
    url: String
) : WhatsAppCloudBotInteractiveMessage = WhatsAppCloudBotInteractiveMessage(
    messagingProduct = "whatsapp",
    recipientType = WhatsAppCloudBotRecipientType.individual,
    interactive = WhatsAppCloudBotInteractive(
        type = WhatsAppCloudBotInteractiveType.cta_url,
        body = WhatsAppCloudBotBody(translate(text).toString()),
        action = WhatsAppCloudBotAction(
            name = "cta_url",
            parameters = ParametersUrl(
                displayText = textButton,
                url = url
            )
        )
    )
)

fun I18nTranslator.listMessage(
    text: CharSequence,
    button: CharSequence,
    vararg replies: QuickReply
) : WhatsAppCloudBotInteractiveMessage =
    listMessage(text, button, replies.toList())

fun I18nTranslator.listMessage(
    text: CharSequence,
    button: CharSequence,
    replies: List
) : WhatsAppCloudBotInteractiveMessage =
    completeListMessage(
        text, button, WhatsAppCloudBotActionSection(rows = replies.map {
        WhatsAppBotRow(
            id = it.payload,
            title = it.title,
        )
    })
    )

fun I18nTranslator.completeListMessage(
    text: CharSequence,
    button: CharSequence,
    vararg sections: WhatsAppCloudBotActionSection
) : WhatsAppCloudBotInteractiveMessage = completeListMessage(text, button, sections.toList())

fun I18nTranslator.completeListMessage(
    text: CharSequence,
    button: CharSequence,
    sections: List,
) : WhatsAppCloudBotInteractiveMessage = WhatsAppCloudBotInteractiveMessage(
    messagingProduct = "whatsapp",
    recipientType = WhatsAppCloudBotRecipientType.individual,
    interactive =  WhatsAppCloudBotInteractive(
        type = WhatsAppCloudBotInteractiveType.list,
        body = WhatsAppCloudBotBody(translate(text).toString()),
        action = WhatsAppCloudBotAction(
            button = translate(button).toString().checkLength(WHATS_APP_BUTTONS_TITLE_MAX_LENGTH),
            sections = sections.map {
                WhatsAppCloudBotActionSection(
                    title = translate(it.title).toString().checkLength(WHATS_APP_SECTION_TITLE_MAX_LENGTH),
                    rows = it.rows?.map { row ->
                        WhatsAppBotRow(
                            id = row.id.checkLength(WHATS_APP_ROW_ID_MAX_LENGTH),
                            title = translate(row.title).toString().checkLength(WHATS_APP_ROW_TITLE_MAX_LENGTH),
                            description = translate(row.description).toString().checkLength(WHATS_APP_ROW_DESCRIPTION_MAX_LENGTH)
                        )
                    }
                )
            }
        )
    )
).also {
    if ((it.interactive.action?.sections?.flatMap { s -> s.rows ?: listOf() }?.count() ?: 0) > WHATS_APP_MAX_ROWS) {
        error("a list message is limited to $WHATS_APP_MAX_ROWS rows across all sections.")
    }
    if ((it.interactive.action?.sections?.count() ?: 0) > WHATS_APP_MAX_SECTIONS) {
        error("sections count in list message should not exceed $WHATS_APP_MAX_SECTIONS.")
    }
    if ((it.interactive.action?.button?.count() ?: 0) > WHATS_APP_BUTTONS_TITLE_MAX_LENGTH) {
        error("button text ${it.interactive.action?.button} should not exceed $WHATS_APP_BUTTONS_TITLE_MAX_LENGTH chars.")
    }
}


fun I18nTranslator.replyLocationMessage(
    text: CharSequence
) : WhatsAppCloudBotInteractiveMessage = WhatsAppCloudBotInteractiveMessage(
    messagingProduct = "whatsapp",
    recipientType = WhatsAppCloudBotRecipientType.individual,
    interactive = WhatsAppCloudBotInteractive(
        type = WhatsAppCloudBotInteractiveType.location_request_message,
        body = WhatsAppCloudBotBody(translate(text).toString()),
        action = WhatsAppCloudBotAction(
            name = "send_location"
        )
    )
)


fun > T.quickReply(
    title: CharSequence,
    targetIntent: IntentAware,
    parameters: Parameters
): QuickReply =
    quickReply(title, targetIntent, stepName, parameters.toMap())

fun > T.quickReply(
    title: CharSequence,
    targetIntent: IntentAware,
    step: StoryStep? = null,
    vararg parameters: Pair
) : QuickReply = quickReply(title, targetIntent.wrappedIntent(), step?.name, parameters.toMap())

fun > T.quickReply(
    title: CharSequence,
    targetIntent: IntentAware,
    step: String? = null,
    parameters: Map = mapOf()
) : QuickReply =
    quickReply(title, targetIntent, step, parameters) { intent, s, params ->
        SendChoice.encodeChoiceId(intent, s, params, null, null, sourceAppId = null)
    }

private fun I18nTranslator.quickReply(
    title: CharSequence,
    targetIntent: IntentAware,
    step: String? = null,
    parameters: Map,
    payloadEncoder: (IntentAware, String?, Map) -> String
) : QuickReply = QuickReply(
    translate(title).toString(),
    payloadEncoder.invoke(targetIntent, step, parameters)
)

fun I18nTranslator.nlpQuickReply(
    title: CharSequence,
    textToSend: CharSequence = title,
) : QuickReply = QuickReply(
    translate(title).toString(),
    SendChoice.encodeNlpChoiceId(translate(textToSend).toString()),
)

fun I18nTranslator.buildTemplateMessage(
    templateName: String,
    languageCode: String,
    components: List
):  WhatsAppCloudBotTemplateMessage {
    return WhatsAppCloudBotTemplateMessage(
        messagingProduct = "whatsapp",
        recipientType = WhatsAppCloudBotRecipientType.individual,
        template = WhatsAppCloudBotTemplate(
            name = templateName,
            language = Language(
                code = languageCode,
            ),
            components = components
        )
    )
}


fun I18nTranslator.buildTemplateMessageCarousel(
    templateName: String,
    components : List,
    languageCode: String
): WhatsAppCloudBotTemplateMessage {
    return WhatsAppCloudBotTemplateMessage(
        messagingProduct = "whatsapp",
        recipientType = WhatsAppCloudBotRecipientType.individual,
        template = WhatsAppCloudBotTemplate(
            name = templateName,
            language = Language(
                code = languageCode,
            ),
            components = listOf(
                Component.Carousel(
                    type = ComponentType.CAROUSEL,
                    cards = components
                )
            )
        )
    )
}


fun > T.cardCarousel(
    cardIndex: Int,
    components: List
): Component.Card = Component.Card(
    cardIndex = cardIndex,
    components = components
)
fun > T.bodyTemplate(
    parameters: List
):Component.Body = Component.Body(
    type = ComponentType.BODY,
    parameters = parameters
)

fun > T.TextParameterTemplate(
    typeParameter:CharSequence?,
    textButton: CharSequence?
):TextParameter = TextParameter(
    type = ParameterType.valueOf(translate(typeParameter).toString()),
    text = translate(textButton).toString(),
)

fun buttonTemplate(
    index: String,
    subType: String,
    parameters: List
):Component.Button = Component.Button(
    type = ComponentType.BUTTON,
    subType = ButtonSubType.valueOf(subType),
    index = index,
    parameters = parameters
)

fun > T.postbackButton(
    index: String,
    textButton: String,
    payload: String?
):Component.Button = buttonTemplate(index, ButtonSubType.QUICK_REPLY.name, listOf(
    payloadParameterTemplate(textButton, payload, ParameterType.PAYLOAD.name)
))

fun > T.whatsAppPostbackButton(
    index: String,
    title: CharSequence,
    targetIntent: IntentAware,
    step: StoryStep? = null,
    parameters: Parameters = Parameters()
):Component.Button = postbackButton(
    index = index,
    textButton = translate(title).toString(),
    targetIntent.let { i -> SendChoice.encodeChoiceId(this, i, step, parameters.toMap()+(index to index)) }
)

fun > T.whatsAppNLPPostbackButton(
    index: String,
    title: CharSequence,
    textToSend: CharSequence = title,
):Component.Button = postbackButton(
    index = index,
    textButton = translate(title).toString(),
    payload = SendChoice.encodeNlpChoiceId(translate(textToSend).toString()),
)

fun > T.whatsAppUrlButton(
    index: String,
    textButton: String,
):Component.Button = buttonTemplate(index, ButtonSubType.URL.name, listOf(
    payloadParameterTemplate(textButton, null,ParameterType.TEXT.name)
))

fun payloadParameterTemplate(
    textButton: String,
    payload: String?,
    typeParameter:String,
):PayloadParameter = PayloadParameter(
    type = ParameterType.valueOf(typeParameter),
    payload = payload,
    text = textButton,
)


fun headerTemplate(
    typeParameter:String,
    imageId: String
):Component.Header = Component.Header(
    type = ComponentType.HEADER,
    parameters = listOf(HeaderParameter.Image(
        type = ParameterType.valueOf(typeParameter),
        image = ImageId(
            id = imageId
        )
    )
    )
)

private fun String.checkLength(maxLength: Int) : String {
    if (maxLength > 0 && this.length > maxLength) {
        logger.info { "text $this should not exceed $maxLength chars." }
    }
    return this
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy