2
votes

I am using Microsoft teams bot with nodejs. I am rendering a carousel of adaptive cards with action on each card. My requirement is to delete an individual card out on which the action was clicked. Is it possible?

Current code looks like below. i have given a try to deleteActive but that deletes entire carousel

const {
    TurnContext,
    TeamsActivityHandler,
    CardFactory,
    AttachmentLayoutTypes,
    ActionTypes
} = require('botbuilder');

class TeamsConversationBot extends TeamsActivityHandler {
    constructor() {
        super();
        this.onMessage(async (context:any, next:any) => {
            TurnContext.removeRecipientMention(context.activity);
            console.log("context activigty at the begin is:" + JSON.stringify(context.activity))
            let msg = context.activity.text
            let action = context.activity.value

            if(msg.startsWith('lead')){
                msg = 'lead'
            }

            if(action !== undefined){
                console.log("user did some action on a card")
                msg = action.action
            }

            switch (msg) {
                case 'lead':
                        await this.lead(context)
                        break;
                case 'qualify_lead':
                        await this.qualifyLead(context)
                        break;
            }
            await next();
        });
    }


    /**
     * 
     * @param context this method does a lead qualification
     */
    async qualifyLead(context:any){
        console.log("in qualifyLead:" + JSON.stringify(context.activity))
        //await context.deleteActivity(context.activity.replyToId)

        const leadId = context.activity.value.objectId
        console.log("Lead to qualify is:" + leadId)


        await context.sendActivity('Lead is qualified')
    }


/**
    * Search contact by name
    * @param context
    * @param keyword 
*/ 
async lead(context:any){
    console.log("Start of lead with context:" + JSON.stringify(context))
    const cardArr = []
    let items = [
        {"Name": 'x', "LeadId": "1"},
        {"Name": 'a', "LeadId": "2"},
        {"Name": 'b', "LeadId": "3"},
        {"Name": 'c', "LeadId": "4"},
        {"Name": 'd', "LeadId": "5"}
    ]

     for(const item of items){
        const header =  {
            "type": "TextBlock",
            "size": "Medium",
            "weight": "Bolder",
            "text": item.Name
        }



    const actions = [
        {
            "type": "Action.Submit",
            "title": "Qualify",
            "data": { "action" : "qualify_lead", "objectId" : item.LeadId }
        }
       ]


   const acard = CardFactory.adaptiveCard(
    {
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
        "type": "AdaptiveCard",
        "version": "1.0",
        "body": [
            header,
            ''
            ],
           "actions": actions
         }
     )

    cardArr.push(acard)
    console.log("payload is::::" + JSON.stringify(acard))
        }

    const reply = {
        "attachments" : cardArr,
        "attachmentLayout" : AttachmentLayoutTypes.Carousel
    }

    await context.sendActivity(reply);
}

}

module.exports.TeamsConversationBot = TeamsConversationBot;
1
Never tried this but I think you would need to remove the clicked value from the cardArr array using cardArr.filter or something similar. You'd have to regenerate the carousel, too.billoverton
hmm sounds like i will need to pass the entire cards payload back do what i need to do and send it back with one less cardMoblize IT
I believe I can help, but can you fix your code sample please? await next(); is duplicated in your onMessage handler, you have extra or missing closing brackets in some places, your card's body has null as one of its elements and "actions" is contained within the body, etc. How can we fix your code if we don't know if these problems are in your actual code or are just artifacts from copying and pasting your code? Make sure you format the code in your editor so that it's easy to read. (Since there are multiple people on this thread, you need to @ mention me so I'll see your reply.)Kyle Delaney
@KyleDelaney i have re added the code by fixing issues and simplifying as much as i could. please adviseMoblize IT
@MoblizeIT - In VS Code you can use alt+shift+F to auto-format your documentKyle Delaney

1 Answers

2
votes

As with this other answer, the answer will be similar to this one. I can see you're trying to use TypeScript but your code deviates very little from JavaScript so I'll just write my answer in JavaScript.

First, you'll need a way of saving state for your [carousel] so you can update the [carousel]'s activity.

this.carouselState = this.conversationState.createProperty('carouselState');

You'll want a consistent way to generate your [carousel] that you can use when you send the [carousel] initially and when you update the [carousel].

createCarousel(batchId, leads)
{
    const cardArr = [];

    let items = [
        { "Name": 'x', "LeadId": 1 },
        { "Name": 'a', "LeadId": 2 },
        { "Name": 'b', "LeadId": 3 },
        { "Name": 'c', "LeadId": 4 },
        { "Name": 'd', "LeadId": 5 }
    ];

    items = items.filter(item => leads.includes(item.LeadId));

    for (const item of items) {
        const header = {
            "type": "TextBlock",
            "size": "Medium",
            "weight": "Bolder",
            "text": item.Name
        };

        const actions = [
            {
                "type": "Action.Submit",
                "title": "Qualify",
                "data": { [KEYACTION]: ACTIONQUALIFYLEAD, [KEYOBJECTID]: item.LeadId, [KEYBATCHID]: batchId }
            }
        ];

        const acard = CardFactory.adaptiveCard(
            {
                "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
                "type": "AdaptiveCard",
                "version": "1.0",
                "body": [
                    header
                ],
                "actions": actions
            }
        );

        cardArr.push(acard);
    }

    return {
        "type": "message",
        "attachments": cardArr,
        "attachmentLayout": AttachmentLayoutTypes.Carousel
    };
}

This is similar to your code but there are some important differences. First, I'm filtering the items array to allow for fewer items, which is how you'll end up deleting cards from your carousel. Second, I'm including a "batch ID" in each action's data, which is how your bot will know which activity to update when it receives the action's payload. Also, this isn't relevant to your question but I'm using string constants instead of string literals most everywhere I expect to use that string more than once, which is a practice I follow to avoid typo-related bugs etc.

Using this function, you can send the [carousel] initially like this

async testCarousel(turnContext) {
    const batchId = Date.now();
    const leads = [1, 2, 3, 4, 5];
    const reply = this.createCarousel(batchId, leads);
    const response = await turnContext.sendActivity(reply);
    const dict = await this.carouselState.get(turnContext, {});

    dict[batchId] = {
        [KEYACTIVITYID]: response.id,
        [KEYLEADS]: leads
    };
}

And you can update the [carousel] in response to the card's [qualify] submit action like this

async handleSubmitAction(turnContext) {
    const value = turnContext.activity.value;

    switch (value[KEYACTION]) {
        case ACTIONQUALIFYLEAD:
            const dict = await this.carouselState.get(turnContext, {});
            const batchId = value[KEYBATCHID];
            const info = dict[batchId];
            if (info) {
                const leads = info[KEYLEADS];
                const objectId = value[KEYOBJECTID];
                var index = leads.indexOf(objectId);
                if (index !== -1) leads.splice(index, 1);
                const update = this.createCarousel(batchId, leads);
                update.id = info[KEYACTIVITYID];
                if (update.attachments.length) {
                    await turnContext.updateActivity(update);
                } else {
                    await turnContext.deleteActivity(update.id);
                }
            }
            break;
    }
}