如何使用OAuthCard基于登录用户在BotFramework-WebChat中动态设置用户头像

问题描述 投票:0回答:1

我使用Microsoft Bot Framework V4开发了一个聊天机器人,并使用BotFramework-WebChat为用户提供使用DirectLine Token从网站聊天,

我可以通过分配静态公共图像URL来设置机器人头像和用户头像。问题是我想使用以下步骤在WebChat中动态设置用户头像

  1. OAuthCard登录后,使用Microsoft图形API获取用户图标
  2. 动态设置Webchat styleSetOptions中的登录用户图像。

我按照提供的示例通过演示设置了机器人框架服务器和机器人的网络聊天

bot server == https://github.com/Microsoft/BotBuilder-Samples

网聊== https://github.com/Microsoft/BotFramework-WebChat

但是没有关于如何在用户登录后使用签名用户对象设置用户图像的正确示例或文档。

任何一点都指向正确的方向,如何实现。

提前致谢

botframework bots direct-line-botframework web-chat
1个回答
1
投票

您可以通过将Graph API调用和结果包装到AAD登录过程的结果中来实现此目的。以下代码基于使用React.Component的BotBuilder-Samples 24.bot-authentication-msgraph sample和BotFramework-WebChat 17.chat-send-history sample

(请注意,当前位于主分支中的Graph示例不包括获取AAD登录用户的照片。我有一个PR将此功能添加到示例中,但我也将其包含在此处。我使用了WebChat样本作为构建以下内容的参考。)

网上聊天

您需要样本#17中的这些文件,然后是需要更改的App.js文件:

  • 公共[文件夹] favicon.ico的 的index.html 的manifest.json
  • src [文件夹] App.js index.css index.js
  • .ENV
  • 的package.json

App.js:

注意:我在一个单独的项目中本地生成直接线令牌。这假定AAD配置文件有照片。

import React from 'react';

import ReactWebChat, { createDirectLine, createStore} from 'botframework-webchat';

export default class extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      directLine: null,
      avatarState: false, // Sets value to false; Is updated to true after login
      // Sets the default styleOptions used during rendering
      styleOptions: {
          botAvatarImage: 'https://docs.microsoft.com/en-us/azure/bot-service/v4sdk/media/logo_bot.svg?view=azure-bot-service-4.0',
          botAvatarInitials: 'BF',
          userAvatarImage: 'https://github.com/compulim.png?size=64',
          userAvatarInitials: 'WC'
        }
    };

    // Creates the listener filtering for a new avatar image and applies to styleOptions
    this.store = createStore(
      {},
      () => next => action => {
        if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
          }
        if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' 
          && action.payload.activity.attachments 
          && 0 in action.payload.activity.attachments
          && this.state.avatarState === false) {
            let attachments = action.payload.activity.attachments;
            if ('content' in attachments[0] && 'images' in attachments[0].content) {
              this.setState(() => ({
                  styleOptions: {
                    userAvatarImage: attachments[0].content.images[0].contentUrl
                  },
                  avatarState: true
              }));
            }
        }

        return next(action);
      }
    )
  }

  componentDidMount() {
    this.fetchToken();
  }

  async fetchToken() {
    const res = await fetch('http://localhost:3979/directline/token', { method: 'POST' });
    const { token } = await res.json();

    this.setState(() => ({
      directLine: createDirectLine({ token })
    }));
  }

  render() {
    return (
      this.state.directLine ?
        <ReactWebChat
          className="chat"
          directLine={ this.state.directLine }
          styleOptions={ this.state.styleOptions }
          store={ this.store }
          { ...this.props }
        />
      :
        <div>Connecting to bot&hellip;</div>
    );
  }
}

package.json

{
  "name": "change-avatar",
  "version": "0.1.0",
  "private": true,
  "homepage": "",
  "dependencies": {
    "botframework-webchat": ">= 0.0.0-0",
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "react-scripts": "2.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "eject": "react-scripts eject"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

MS Graph Bot

更新示例#24中的以下文件:

bot.js:

async processStep替换为:

async processStep(step) {
    // We do not need to store the token in the bot. When we need the token we can
    // send another prompt. If the token is valid the user will not need to log back in.
    // The token will be available in the Result property of the task.
    const tokenResponse = step.result;

    // If the user is authenticated the bot can use the token to make API calls.
    if (tokenResponse !== undefined) {
        let parts = await this.commandState.get(step.context);
        if (!parts) {
            parts = step.context.activity.text;
        }
        const command = parts.split(' ')[0].toLowerCase();
        if (command === 'me') {
            await OAuthHelpers.listMe(step.context, tokenResponse);
        } else if (command === 'send') {
            await OAuthHelpers.sendMail(step.context, tokenResponse, parts.split(' ')[1].toLowerCase());
        } else if (command === 'recent') {
            await OAuthHelpers.listRecentMail(step.context, tokenResponse);
        } else {
            let photoResponse = await OAuthHelpers.loginData(step.context, tokenResponse);
            const card = CardFactory.heroCard(
                `Welcome ${ photoResponse.displayName }, you are now logged in.`,
                [photoResponse],
                []
            );
            const reply = ({ type: ActivityTypes.Message });
            reply.attachments = [card];
            await step.context.sendActivity(reply);
        }
    } else {
        // Ask the user to try logging in later as they are not logged in.
        await step.context.sendActivity(`We couldn't log you in. Please try again later.`);
    }
    return await step.endDialog();
};

oauth-helpers.js:

添加static async loginData

/**
 * Displays information about the user in the bot.
 * @param {TurnContext} turnContext A TurnContext instance containing all the data needed for processing this conversation turn.
 * @param {TokenResponse} tokenResponse A response that includes a user token.
 */
static async loginData(turnContext, tokenResponse) {
    if (!turnContext) {
        throw new Error('OAuthHelpers.loginData(): `turnContext` cannot be undefined.');
    }
    if (!tokenResponse) {
        throw new Error('OAuthHelpers.loginData(): `tokenResponse` cannot be undefined.');
    }

    try {
        // Pull in the data from Microsoft Graph.
        const client = new SimpleGraphClient(tokenResponse.token);
        const me = await client.getMe();
        const photoResponse = await client.getPhoto();

        // Attaches user's profile photo to the reply activity.
        if (photoResponse != null) {
            let replyAttachment;
            const base64 = Buffer.from(photoResponse, 'binary').toString('base64');
            replyAttachment = {
                contentType: 'image/jpeg',
                contentUrl: `data:image/jpeg;base64,${ base64 }`
            };
            replyAttachment.displayName = me.displayName;
            return (replyAttachment);
        }
    } catch (error) {
        throw error;
    }
}

simple-graph-client.js:

添加async getPhoto

/**
 * Collects the user's photo.
 */
async getPhoto() {
    return await this.graphClient
        .api('/me/photo/$value')
        .responseType('ArrayBuffer')
        .version('beta')
        .get()
        .then((res) => {
            return res;
        })
        .catch((err) => {
            console.log(err);
        });
}

package.json:

确保@microsoft/microsoft-graph-client安装版本1.0.0,因为在后续版本中围绕AAD'displayName'获取的更改。

一旦上面的代码实现,我就能够登录,一旦成功,立即更新用户头像。

希望有所帮助!

© www.soinside.com 2019 - 2024. All rights reserved.