question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

getUploadedAttachment - DialogContextError: Cannot read property 'createConnectorClient' of undefined

See original GitHub issue

Versions

nodejs version: v14.17.0

Browser Version: Microsoft Edge Version 91.0.864.37 (Official build) (64-bit)

OS: Windows 10 Enterprise OS Build : 19043.985 Experience : Windows Feature Experience Pack 120.2212.2020.0

Other dependencies:

“name”: “teams-conversation-bot”, “version”: “1.0.0”, “msteams”: { “teamsAppId”: null

“description”: “Microsoft Teams conversation bot quickstart”, “author”: “Microsoft”, “license”: “MIT”, “main”: “index.js”, “scripts”: { “start”: “node ./index.js”, “watch”: “nodemon ./index.js”

“dependencies”: { “@microsoft/teamsfx”: “^0.1.0”, “@microsoft/microsoft-graph-client”: “^2.2.1”, “botbuilder”: “~4.11.0”, “botbuilder-dialogs”: “~4.11.0”, “isomorphic-fetch”: “^3.0.0”, “restify”: “~8.5.1”

“devDependencies”: { “ngrok”: “^3.4.0”, “nodemon”: “^2.0.7”

Describe the bug

I’m trying to implement the getUploadedAttachment() present in this example : https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/15.handling-attachments and it seems is working in stand-alone mode and Bot Framework emulator 4, but if I try to implement inside this other example https://docs.microsoft.com/en-us/microsoftteams/platform/get-started/first-app-bot?tabs=vscode that allow to test it inside the TEAMS (what ever chat or teams) is not working with the following errors:

Server Side: [onTurnError] unhandled error: DialogContextError: Cannot read property ‘createConnectorClient’ of undefined

Teams Side: The bot encountered an unhandled error: Cannot read property ‘createConnectorClient’ of undefined To continue to run this bot, please fix the bot source code.

Code Used

const { ChoicePrompt,ComponentDialog, DialogSet, DialogTurnStatus, WaterfallDialog } = require("botbuilder-dialogs");
const { TurnContext, ActivityHandler, ActivityTypes,ActionTypes, CardFactory, TextFormatTypes } = require("botbuilder");
const path = require('path');
const axios = require('axios');
const fs = require('fs');

const AdaptiveCard = require('../resources/adaptiveCard.json');

class RootDialog extends ComponentDialog {
  constructor(id) {
    super(id);

    
 }

  async onBeginDialog(innerDc, options) {
    const result = await this.triggerCommand(innerDc);
    if (result) {
      return result;
    }

    return await super.onBeginDialog(innerDc, options);
  }

  async onContinueDialog(innerDc) {
    return await super.onContinueDialog(innerDc);
  }




  async triggerCommand(innerDc) {
    const removedMentionText = TurnContext.removeRecipientMention(
      innerDc.context.activity,
      innerDc.context.activity.recipient.id
    );
    let text = "";
    

    if (removedMentionText) {
      text = removedMentionText.toLowerCase().replace(/\n|\r/g, "").trim(); // Remove the line break
    }

    if (innerDc.context.activity.textFormat !== TextFormatTypes.Plain) {
      return await innerDc.cancelAllDialogs();
    }

    switch (text) {
      case "show": {
        if (innerDc.context.activity.conversation.isGroup) {
          await innerDc.context.sendActivity(
            `Sorry, currently TeamsFX SDK doesn't support Group/Team/Meeting Bot SSO. To try this command please install this app as Personal Bot and send "show".`
          );
          return await innerDc.cancelAllDialogs();
        }
        break;
      }
      case "ciao": {
        if (innerDc.context.activity.conversation.isGroup) {
          await innerDc.context.sendActivity(
            `ciao sono io topolino`
          );
          return await innerDc.cancelAllDialogs();
        }
        break;
      }
      case "intro": {
        const cardButtons = [
          {
            type: ActionTypes.ImBack,
            title: "Show profile",
            value: "show",
          },
        ];
        const card = CardFactory.heroCard("Introduction", null, cardButtons, {
          text: `This Bot has implemented single sign-on (SSO) using the identity of the user signed into the Teams client. See the <a href="https://aka.ms/teamsfx-docs-auth">TeamsFx authentication document</a> and code in <pre>bot/dialogs/mainDialog.js</pre> to learn more about SSO.<br>Type <strong>show</strong> or click the button below to show your profile by calling Microsoft Graph API with SSO. To learn more about building Bot using Microsoft Teams Framework, please refer to the <a href="https://aka.ms/teamsfx-docs">TeamsFx documentation</a>.`,
        });
        await innerDc.context.sendActivity({ attachments: [card] });
        return await innerDc.cancelAllDialogs();
      }
      case "juve": {
        const card = CardFactory.heroCard(
          'Welcome to Bot Juventus Fan!',
          'You are great Juventus Support! Just Remember #UntilTheEndForzaJuventus# #finoallafineForzaJuventus#',
          ['https://sfondo.info/i/original/d/9/f/29503.jpg'],
          [
              {
                  type: ActionTypes.OpenUrl,
                  title: 'Go on Juventus Webisite',
                  value: 'https://www.juventus.com/it'
              },
              {
                  type: ActionTypes.OpenUrl,
                  title: 'Go on Juventus Store',
                  value: 'https://store.juventus.com/'
              }
          ]
      );
        await innerDc.context.sendActivity({ attachments: [card] });
        return await innerDc.cancelAllDialogs();
      }
      case "juve2": {
        await innerDc.context.sendActivity({ attachments: [this.createVideoCard()] });
        //await innerDc.context.sendActivity({ attachments: [card] });
        return await innerDc.cancelAllDialogs();
      }
      case "1":{
        const reply = { type: ActivityTypes.Message };
        reply.text = 'This is an inline attachment.';
        reply.attachments = [this.getInlineAttachment()];

        await innerDc.context.sendActivity(reply);
        return await innerDc.cancelAllDialogs();

      }
      case "2":{
        const reply = { type: ActivityTypes.Message };
        reply.attachments = [await this.getUploadedAttachment(text)];
        reply.text = 'This is an uploaded attachment.';


        await innerDc.context.sendActivity(reply);
        return await innerDc.cancelAllDialogs();

      }
      case "3":{
        const reply = { type: ActivityTypes.Message };
        reply.text = 'This is an internet attachment.';
        reply.attachments = [this.getInternetAttachment(text)];

        await innerDc.context.sendActivity(reply);
        return await innerDc.cancelAllDialogs();

      }
      default: {
        const cardButtons = [
          {
            type: ActionTypes.ImBack,
            title: "Show introduction card",
            value: "intro",
          },
        ];
        const card = CardFactory.heroCard("", null, cardButtons, {
          text: `This is a hello world Bot built with Microsoft Teams Framework, which is designed for illustration purposes. This Bot by default will not handle any specific question or task.<br>Please type <strong>intro</strong> to see the introduction card.`,
        });
        await innerDc.context.sendActivity({ attachments: [card] });
        return await innerDc.cancelAllDialogs();
      }
    }
  }


  async getUploadedAttachment(text) {
    const imageData = fs.readFileSync(path.join(__dirname, '../resources/architecture-resize.png'));
    const connector = text.adapter.createConnectorClient(text.activity.serviceUrl);
    
    const conversationId = text.activity.conversation.id;
    const response = await connector.conversations.uploadAttachment(conversationId, {
        name: 'architecture-resize.png',
        originalBase64: imageData,
        type: 'image/png'
    });
  
    // Retrieve baseUri from ConnectorClient for... something.
    const baseUri = connector.baseUri;
    const attachmentUri = baseUri + (baseUri.endsWith('/') ? '' : '/') + `v3/attachments/${ encodeURI(response.id) }/views/original`;
    return {
        name: 'architecture-resize.png',
        contentType: 'image/png',
        contentUrl: attachmentUri
    };
  }

  async handleIncomingAttachment(text) {
    // Prepare Promises to download each attachment and then execute each Promise.
    const promises = text.activity.attachments.map(this.downloadAttachmentAndWrite);
    const successfulSaves = await Promise.all(promises);

    // Replies back to the user with information about where the attachment is stored on the bot's server,
    // and what the name of the saved file is.
    async function replyForReceivedAttachments(localAttachmentData) {
        if (localAttachmentData) {
            // Because the TurnContext was bound to this function, the bot can call
            // `TurnContext.sendActivity` via `this.sendActivity`;
            await this.sendActivity(`Attachment "${ localAttachmentData.fileName }" ` +
                `has been received and saved to "${ localAttachmentData.localPath }".`);
        } else {
            await this.sendActivity('Attachment was not successfully saved to disk.');
        }
    }

    // Prepare Promises to reply to the user with information about saved attachments.
    // The current TurnContext is bound so `replyForReceivedAttachments` can also send replies.
    const replyPromises = successfulSaves.map(replyForReceivedAttachments.bind(text));
    await Promise.all(replyPromises);
}
    /**
     * Downloads attachment to the disk.
     * @param {Object} attachment
     */
     async downloadAttachmentAndWrite(attachment) {
      // Retrieve the attachment via the attachment's contentUrl.
      const url = attachment.contentUrl;

      // Local file path for the bot to save the attachment.
      const localFileName = path.join(__dirname, attachment.name);

      try {
          // arraybuffer is necessary for images
          const response = await axios.get(url, { responseType: 'arraybuffer' });
          // If user uploads JSON file, this prevents it from being written as "{"type":"Buffer","data":[123,13,10,32,32,34,108..."
          if (response.headers['content-type'] === 'application/json') {
              response.data = JSON.parse(response.data, (key, value) => {
                  return value && value.type === 'Buffer' ? Buffer.from(value.data) : value;
              });
          }
          fs.writeFile(localFileName, response.data, (fsError) => {
              if (fsError) {
                  throw fsError;
              }
          });
      } catch (error) {
          console.error(error);
          return undefined;
      }
      // If no error was thrown while writing to disk, return the attachment's name
      // and localFilePath for the response back to the user.
      return {
          fileName: attachment.name,
          localPath: localFileName
      };
  }

  getInlineAttachment() {
    const imageData = fs.readFileSync(path.join(__dirname, '../resources/architecture-resize.png'));
    const base64Image = Buffer.from(imageData).toString('base64');

    return {
        name: 'architecture-resize.png',
        contentType: 'image/png',
        contentUrl: `data:image/png;base64,${ base64Image }`
    };
}

getInternetAttachment() {
  // NOTE: The contentUrl must be HTTPS.
  return {
      name: 'architecture-resize.png',
      contentType: 'image/png',
      contentUrl: 'https://docs.microsoft.com/en-us/bot-framework/media/how-it-works/architecture-resize.png'
  };
}

createVideoCard() {
  return CardFactory.videoCard(
      '2018 Imagine Cup World Championship Intro',
      [{ url: 'https://sec.ch9.ms/ch9/783d/d57287a5-185f-4df9-aa08-fcab699a783d/IC18WorldChampionshipIntro2.mp4' }],
      [{
          type: 'openUrl',
          title: 'Lean More',
          value: 'https://channel9.msdn.com/Events/Imagine-Cup/World-Finals-2018/2018-Imagine-Cup-World-Championship-Intro'
      }],
      {
          subtitle: 'by Microsoft',
          text: 'Microsoft\'s Imagine Cup has empowered student developers around the world to create and innovate on the world stage for the past 16 years. These innovations will shape how we live, work and play.'
      }
  );
}

}



module.exports.RootDialog = RootDialog;

//module.exports.AttachmentsBot = AttachmentsBot;      

Expected behavior

Return correctly the attachment

Screenshots

If applicable, add screenshots to help explain your problem.

Additional context

Node JS + Visual Studio Code

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:14 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
joshgummersallcommented, Jun 3, 2021

@Gnux17, looking at the code you’ve included, I think I found the problem. You seem to be passing text (a string) to methods expecting a TurnContext. Check out this diff to see if that resolves your problem.

0reactions
joshgummersallcommented, Jun 8, 2021

I’ve opened a PR in the repo you shared. Let’s continue debugging there.

EDIT: PR is here: https://github.com/Gnux17/MixingBots-Teams/pull/1

Read more comments on GitHub >

github_iconTop Results From Across the Web

DialogContextError in botbuilder-dialogs · Issue #3191 - GitHub
Every time the user tries to start a new conversation with the bot a DialogContextError is being thrown and the dialog cannot be...
Read more >
Bot Framework SDK 4.11 DialogContextError: Cannot read ...
Below error encountered after updating to version 4.11.0 { TypeError: Cannot read property 'trackTrace' of undefined at Object.
Read more >
botbuilder-dialogs package - Microsoft Learn
A container for a set of Dialogs. DialogContext. The context for the current dialog turn with respect to a specific DialogSet. DialogContextError.
Read more >
botbuilder-js issues and how to fix | GitAnswer
getUploadedAttachment - DialogContextError: Cannot read property 'createConnectorClient' of undefined - TypeScript botbuilder-js ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found