GithubHelp home page GithubHelp logo

microsoft / botframework-directlinejs Goto Github PK

View Code? Open in Web Editor NEW
196.0 43.0 126.0 1.69 MB

JavaScript client library for Microsoft Bot Framework's Direct Line protocol

License: MIT License

TypeScript 59.88% JavaScript 38.26% HTML 1.86%

botframework-directlinejs's Introduction

Bot Framework DirectLineJS

Microsoft Bot Framework Direct Line JS Client

Build Status

Client library for the Microsoft Bot Framework Direct Line protocol.

Used by WebChat and thus (by extension) Emulator, WebChat channel, and Azure Bot Service.

FAQ

Who is this for?

Anyone who is building a Bot Framework JavaScript client who does not want to use WebChat.

If you're currently using WebChat, you don't need to make any changes as it includes this package.

What is that funny subscribe() method in the samples below?

Instead of callbacks or Promises, this library handles async operations using Observables. Try it, you'll like it! For more information, check out RxJS.

Can I use TypeScript?

You bet.

How ready for prime time is this library?

This is an official Microsoft-supported library, and is considered largely complete. Future changes (aside from supporting future updates to the Direct Line protocol) will likely be limited to bug fixes, performance improvements, tutorials, and samples. The big missing piece here is unit tests.

That said, the public API is still subject to change.

Why the library did not detect Web Socket disconnections?

On iOS/iPadOS, when network change from Wi-Fi to cellular, the WebSocket object will be stalled without any errors. This is not detectable nor workaroundable without any additional assistance. The issue is related to an experimental feature named "NSURLSession WebSocket". The feature is enabled by default on iOS/iPadOS 15 and up.

An option named networkInformation can be used to assist the library to detect any connection issues. The option is based on W3C Network Information API and it should implement at least 2 members:

  • A type property to indicate the current network type
    • When the type is "offline", network is not available and no connection will be made
  • A change event should dispatch when the type property change

However, Safari on iOS/iPadOS does not support W3C Network Information API. It is up to web developers to implement the NetworkInformation polyfill.

One effective way to detect network type change is to subscribe to a Server-Sent Events source. The service would send a message every 30 seconds. If network type changed and current network type is no longer available, the connection will be closed prematurely and an error event will be dispatched to the EventSource instance. Upon receiving the error event, the NetworkInformation.type should then change to "offline". The browser would automatically retry the Server-Sent Events connection. Upon receiving an open event, the polyfill should change the type back to "unknown".

If the library is being used in a native iOS/iPadOS app, a less resource-intensive solution would be partially implementing the Network Information API using NWPathMonitor. When network change happens, the NetworkInformation instance should update the type property based on network type and dispatch a change event.

How to build from source

  1. Clone this repo
  2. npm install
  3. npm run build (or npm run watch to rebuild on every change, or npm run prepublishOnly to build production)

How to include in your app

There are several ways:

  1. Build from scratch and include either /directLine.js (webpacked with rxjs) or lib/directline.js in your app
  2. npm install botframework-directlinejs

Using from within a Node environment

This library uses RxJs/AjaxObserverable which is meant for use in a DOM environment. That doesn't mean you can't also use it from Node though, you just need to do a couple of extra things:

  1. npm install --save ws xhr2
  2. Add the following towards the top of your main application file:
global.XMLHttpRequest = require('xhr2');
global.WebSocket = require('ws');

How to create and use a directLine object

Obtain security credentials for your bot:

  1. If you haven't already, register your bot.
  2. Add a DirectLine (not WebChat) channel, and generate a Direct Line Secret. Make sure Direct Line 3.0 is enabled.
  3. For testing you can use your Direct Line Secret as a security token, but for production you will likely want to exchange that Secret for a Token as detailed in the Direct Line documentation.

Create a DirectLine object:

import { DirectLine } from 'botframework-directlinejs';
// For Node.js:
// const { DirectLine } = require('botframework-directlinejs');

var directLine = new DirectLine({
    secret: /* put your Direct Line secret here */,
    token: /* or put your Direct Line token here (supply secret OR token, not both) */,
    domain: /* optional: if you are not using the default Direct Line endpoint, e.g. if you are using a region-specific endpoint, put its full URL here */
    webSocket: /* optional: false if you want to use polling GET to receive messages. Defaults to true (use WebSocket). */,
    pollingInterval: /* optional: set polling interval in milliseconds. Defaults to 1000 */,
    timeout: /* optional: a timeout in milliseconds for requests to the bot. Defaults to 20000 */,
    conversationStartProperties: { /* optional: properties to send to the bot on conversation start */
        locale: 'en-US'
    }
});

Post activities to the bot:

directLine
  .postActivity({
    from: { id: 'myUserId', name: 'myUserName' }, // required (from.name is optional)
    type: 'message',
    text: 'a message for you, Rudy'
  })
  .subscribe(
    id => console.log('Posted activity, assigned ID ', id),
    error => console.log('Error posting activity', error)
  );

You can also post messages with attachments, and non-message activities such as events, by supplying the appropriate fields in the activity.

Listen to activities sent from the bot:

directLine.activity$.subscribe(activity => console.log('received activity ', activity));

You can use RxJS operators on incoming activities. To see only message activities:

directLine.activity$
  .filter(activity => activity.type === 'message')
  .subscribe(message => console.log('received message ', message));

Direct Line will helpfully send your client a copy of every sent activity, so a common pattern is to filter incoming messages on from:

directLine.activity$
  .filter(activity => activity.type === 'message' && activity.from.id === 'yourBotHandle')
  .subscribe(message => console.log('received message ', message));

Monitor connection status

Subscribing to either postActivity or activity$ will start the process of connecting to the bot. Your app can listen to the connection status and react appropriately :

import { ConnectionStatus } from 'botframework-directlinejs';

directLine.connectionStatus$.subscribe(connectionStatus => {
  switch (connectionStatus) {
    case ConnectionStatus.Uninitialized: // the status when the DirectLine object is first created/constructed
    case ConnectionStatus.Connecting: // currently trying to connect to the conversation
    case ConnectionStatus.Online: // successfully connected to the converstaion. Connection is healthy so far as we know.
    case ConnectionStatus.ExpiredToken: // last operation errored out with an expired token. Your app should supply a new one.
    case ConnectionStatus.FailedToConnect: // the initial attempt to connect to the conversation failed. No recovery possible.
    case ConnectionStatus.Ended: // the bot ended the conversation
  }
});

Reconnect to a conversation

If your app created your DirectLine object by passing a token, DirectLine will refresh that token every 15 minutes. Should your client lose connectivity (e.g. close laptop, fail to pay Internet access bill, go under a tunnel), connectionStatus$ will change to ConnectionStatus.ExpiredToken. Your app can request a new token from its server, which should call the Reconnect API. The resultant Conversation object can then be passed by the app to DirectLine.

var conversation = /* a Conversation object obtained from your app's server */;
directLine.reconnect(conversation);

Resume an existing conversation

When using DirectLine with WebChat, closing the current tab or refreshing the page will create a new conversation in most cases. You can resume an existing conversation to keep the user in the same context.

When using a secret you can resume a conversation by:

  • Storing the conversationid (in a permanent place, like local storage)
  • Giving this value back while creating the DirectLine object along with the secret
import { DirectLine } from 'botframework-directlinejs';

const dl = new DirectLine({
    secret: /* SECRET */,
    conversationId: /* the conversationid you stored from previous conversation */
});

When using a token you can resume a conversation by:

  • Storing the conversationid and your token (in a permanent place, like local storage)
  • Calling the DirectLine reconnect API yourself to get a refreshed token and a streamurl
  • Creating the DirectLine object using the ConversationId, Token, and StreamUrl
import { DirectLine } from 'botframework-directlinejs';

const dl = new DirectLine({
    token: /* the token you retrieved while reconnecting */,
    streamUrl: /* the streamUrl you retrieved while reconnecting */,
    conversationId: /* the conversationid you stored from previous conversation */
});

Getting any history that Direct Line has cached : you can retrieve history using watermarks: You can see the watermark as an activity 'bookmark'. The resuming scenario will replay all the conversation activities from the watermark you specify.

import { DirectLine } from 'botframework-directlinejs';

const dl = new DirectLine({
    token: /* the token you retrieved while reconnecting */,
    streamUrl: /* the streamUrl you retrieved while reconnecting */,
    conversationId: /* the conversationid you stored from previous conversation */,
    watermark: /* a watermark you saved from a previous conversation */,
    webSocket: false
});

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

Reporting Security Issues

Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) at [email protected]. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the Security TechCenter.

Copyright (c) Microsoft Corporation. All rights reserved.

botframework-directlinejs's People

Contributors

a-b-r-o-w-n avatar billba avatar ckkashyap avatar ckkmicrosoft avatar compulim avatar cwhitten avatar danmarshall avatar eanders-ms avatar ericdahlvang avatar gabog avatar jameslew avatar jeffders avatar juharris avatar meulta avatar microsoft-github-policy-service[bot] avatar nt-7 avatar orgads avatar photodesignch avatar sgellock avatar stevengum avatar swagatmishra2007 avatar timenick avatar tobybrad avatar woasishen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

botframework-directlinejs's Issues

Issue with connectionStatus

Hi,

I've been following the docs and get some unexpected behaviour when trying to monitor the connection status...

directLine.connectionStatus$
.subscribe(connectionStatus =>
    switch(connectionStatus) {
        case ConnectionStatus.Uninitialized:    // the status when the DirectLine object is first created/constructed
        case ConnectionStatus.Connecting:       // currently trying to connect to the conversation
        case ConnectionStatus.Online:           // successfully connected to the converstaion. Connection is healthy so far as we know.
        case ConnectionStatus.ExpiredToken:     // last operation errored out with an expired token. Your app should supply a new one.
        case ConnectionStatus.FailedToConnect:  // the initial attempt to connect to the conversation failed. No recovery possible.
        case ConnectionStatus.Ended:            // the bot ended the conversation
    }
);

Firstly is there a typo, should it be case connectionStatus rather than case ConnectionStatus?

Secondly when I console log each case I get back a numbers 0 then 1 and then 2.
Does 0 represent connectionStatus.Uninitialized,
1 represent connectionStatus.Connecting,
and 2 represent connectionStatus.Online?
Or am I doing something wrong/missing something?

Thanks

Documentation around suggestedActions

In the release notes for 0.9.4 it mentions "adds support for suggestedActions in messages"

Is there any documentation around what this is and how to implement it?

I couldn't find any info about suggestedActions in the main Bot Framework documentation.

Thanks

An exception happened occasionally.

Hi, I use this JS in backend server to connect bot framework in the websocket way.
But occasionally (once per day usually) it thorws the error as below and as along as it happends ,the postActivity function will always throw this error. Is it a bug or something I did wrong?
image

502 Bad gateway on sending activities

From 10 mins ago I am receiving 502 on sending activities for all my bots through direct line.

The request response says:

"message:"Request failed with status code 502"

I am sure is not an error on the config since it was working before.
My Bot id is: 9b4c7151-8a92-4b36-976e-8932ec047717

Post Activity

Hey guys, I have a code which posts activity to the directlinejs.
`directLine
.postActivity({

from: { id: 'myUserId', name: 'myUserName' },
type: 'message',
text: 'a message for you, Rudy'

}).subscribe(
id => console.log("Posted activity, assigned ID ", id),
error => console.log("Error posting activity", error)
);`

Now, can I add something like newtext:'some new text' inside postActivity method.

Resume conversation

Per microsoft/BotFramework-WebChat#353, it would be useful to resume a conversation in progress. Let's talk about how this could happen.

First of all we'd need the conversationId of the previous conversation. @dandriscoll can we depend on this remaining valid?

Instead of calling /conversation we call /conversations/{conversationId}, which would give us a current streamUrl. I guess if this call fails we know the conversationId is no longer valid.

We could also optionally supply a watermark. If we don't we'd get all the cached messages Direct Line might still be holding, which would sometimes be desirable.

Then business would resume as normal.

From an API perspective we'd just need to add conversationId?: string and watermark?: string to DirectLineOptions.

You might ask, how do we get the conversationId/watermark from the previous conversation? Well it's inside the DirectLine object. So the hosting page could create a DirectLine object, e.g.

const dl = new DirectLine({
    secret: your_direct_line_secret // or token: your_direct_line_token
});

and then save dl.conversationId and dl.watermark to a cookie, or local storage, or its server, etc. Then when reconstituting it, it would do:

const dl = new DirectLine({
    secret: your_direct_line_secret, // or token: your_direct_line_token
    conversationId: from_previous_conversation,
    watermark: from_previous_converation
})

Am I missing anything?

Support for bot error HTTP status code

Direct Line now returns an httpStatusCode field in the error JSON accompanying HTTP 502s caused by bot timeouts/errors. Is it possible to wire this up so clients have an easy time seeing the bot's HTTP status code?

Example below illustrates a bot returning HTTP 404.


HTTP/1.1 502
...

{
  "error": {
    "code": "ServiceError",
    "message": "Failed to send activity: bot returned an error"
  },
  "httpStatusCode": 404
}

Running on Node - Server to Server with Multiple Conversations?

In trying to integrate our own site chat system with the bot framework, without being able to write a custom channel we have tried to use the directline api via a nodejs 'proxy'.

The directlineJS object is obviously designed for client side where one instance = one conversation. Does anyone have a good methodology for proxying multiple conversations? I could build multiple directline objects, each with their own convesationId, but that wouldnt scale very far as multiple polling get/websocket connections would be created to the directline endpoint?

How can I get those Id dynamically from user??

I am using Directline in bot framework but I get always the same user id and name in activity in Message Controller.

From-id: "default-user"
From-Name: "User"

How can I get those Id dynamically from user??

BotChat.App({
        directLine: { secret: 'xxxx' },
        user: { id: 'userID' ,name = "userName"},
        bot: { id: 'botid' },
        resize: 'detect'
    }, document.getElementById("bot"));

ws.onerror

Hi,

I was wondering is there any reason why there is no onerror method on the observable websocket. I am also using BotFramework-Webchat and I am wondering if there is any recovery planned for if the socket fails.

I see this code in the Chat.tsx file of the Webchat

this.activitySubscription = botConnection.activity$.subscribe(
activity => this.handleIncomingActivity(activity),
error => konsole.log("activity$ error", error)
);

Which means that even if there is an error on the socket and I would like to create a new one I will need to update the activity subscription in the Chat.

Is there a recovery mechanism in place that I am missing?

The issue is mostly about the error recovery in the client webscoket I guess once this is resolved I can update the webchat accordingly. Hence opening the issue here.

Thanks

Get token without exposing secret publicly in Javascript

I'm creating custom chat window on a wordpress site (hosted outside Azure) for my bot using Direct line connector and Javascript, and to start conversation I need to specify Direct line SECRET or a TOKEN for my bot app.
To get a token i have to make a REST call to https://directline.botframework.com/v3/directline/tokens/generate and add to header "Authorization : Bearer SECRET".
I don't want to expose that SECRET publicly in my javascript file and I don't want to pass it as a URL parameter.
What are my other options?
How to get token without exposing my bot Direct line SECRET to client?

Suggestion: update connection status when connection is lost

Trying to test loosing connection

this.directLine.connectionStatus$ .subscribe(connectionStatus => { return this.connectionSource.next(connectionStatus); });

Strange but it is not getting called when I drop of wifi. Any idea?
Using inside Angular 4

Thanks
Nick

Timeout 500: ECONNECTION timeout with some load with new state API implemented

Intro

Since the moment we started using the bot framework we have had random issues where the messages and the conversation requests returns 500 errors. We are about to go to production with the current bot and we did a load test to see how good or bad the bot can behave in different loads.

Setup

Here I will comment the findings to see if they can help to understand where the bottleneck is. Basically our setup is:

  • We have an azure server with the bot built with botbuilder in nodejs
  • The storage we are using is tablestorage. It works properly as I checked the messages with Storage Explorer

Code and calls

To build our clients, we have created in our server requests to send info to directline. Those requests to the bot are:

  • /bot/messages -> The bot runs under this url
  • /bot/message -> Sends a certain message to the bot. Attached the code.
BotHttpRequest.use(BOT_URL)
            .url(`/conversations/${message.conversationId}/activities`)
            .method('POST')
            .headers({
                Authorization: `Bearer ${directLineToken}`
            })
            .body({
                text: message.message,
                type: message.messageType,
                from: {
                    id: currentUser.registrationId,
                    name: currentUser.name
                }
            })
            .retries(3)
            .send()
            .then(function (response) {
                return response.data
            })
  • /bot/convesation -> Starts a new conversation with the bot. The code is as follows:
BotHttpRequest.use(BOT_URL)
            .url('/conversations')
            .method('POST')
            .headers({
                Authorization: `Bearer ${directLineAuth}`
            })
            .send()
            .then(function (response) {
                return response.data
            })
            .then((data) => {
              // Send empty message so the conversationUpdate is triggered on the bot and adds the current user.
              // Otherwise, the current user will not be in the conversation and the greetings will not be triggered.
              const message = new BotMessageRequest({
                message: "",
                conversationId: data.conversationId,
                messageType: "message"
              })
              return this.sendMessage(message, currentUser, `BotConnector ${data.token}`).then(() => {
                return data
              })
            })

All this code works for small amount of requests but as long as it grows a bit (not too much), it just gets broken.

Issues

With this setup we did a test with a middle size machine from azure and this are the findings (test done with jmeter, 300 users, 5 requests per user):

  • 64.81% of requests OK vs 35.19 KO
  • 100% of the errors where error 500
  • The response time per message is around 16s
  • Most of the errors are ECONNECTION Timeout 15000ms (we also tried to increase the timeout with the same result)
  • The total amount of requests done where 4262

Some errors we have seen in the server:
Error 1

ActivityId: 47bb6c41-0000-0000-0000-000000000000, Request URI: /apps/63e70998-1c00-470e-8093-b4ef0dac16b1/services/be412ef7-0aa4-462c-90cc-2ae92aeb435e/partitions/7b23a94a-4102-4a9e-9333-ee80a73e77fe/replicas/131556435222707878p/, RequestStats: , SDK: Microsoft.Azure.Documents.Common/1.19.162.2
    at Function.MongoError.create (D:\home\site\wwwroot\future-street\node_modules\mongodb-core\lib\error.js:31:11)
    at D:\home\site\wwwroot\future-street\node_modules\mongodb-core\lib\connection\pool.js:497:72
    at authenticateStragglers (D:\home\site\wwwroot\future-street\node_modules\mongodb-core\lib\connection\pool.js:443:16)
    at Connection.messageHandler (D:\home\site\wwwroot\future-street\node_modules\mongodb-core\lib\connection\pool.js:477:5)
    at TLSSocket.<anonymous> (D:\home\site\wwwroot\future-street\node_modules\mongodb-core\lib\connection\connection.js:331:22)
    at ZoneDelegate.invokeTask (D:\home\site\wwwroot\future-street\node_modules\zone.js\dist\zone-node.js:275:35)
    at Zone.runTask (D:\home\site\wwwroot\future-street\node_modules\zone.js\dist\zone-node.js:151:47)
    at TLSSocket.ZoneTask.invoke (D:\home\site\wwwroot\future-street\node_modules\zone.js\dist\zone-node.js:345:33)
    at emitOne (events.js:116:13)
    at TLSSocket.emit (events.js:211:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at TLSSocket.Readable.push (_stream_readable.js:208:10)
    at TLSWrap.onread (net.js:594:20)

Error 2

Error: connect ETIMEDOUT 40.127.139.252:443
    at AppInsightsAsyncCorrelatedErrorWrapper.ZoneAwareError (D:\home\site\wwwroot\future-street\node_modules\zone.js\dist\zone-node.js:811:33)
    at new AppInsightsAsyncCorrelatedErrorWrapper (D:\home\site\wwwroot\future-street\node_modules\applicationinsights\out\AutoCollection\CorrelationContextManager.js:172:18)
    at Object._errnoException (util.js:1024:11)
    at _exceptionWithHostPort (util.js:1046:20)

(We also did a test disabling the bot insights correlation with no change on this)

In the report attached you will also see create anonymous and login, those are calls for our and those doesn't have any impact on the test.

Please let us know a way to reduce those numbers since those are not acceptable for a production ready bot.

Thanks for the support and best regards.
Bot performance test result

Node.JS support

I've tried to run sample code from Readme in Angular2 and it works perfectly.

However it doesn't work in NodeJS.

I've added code to track connection status, here is what I got in console.

the status when the DirectLine object is first created/constructed
currently trying to connect to the conversation

no other errors or messages in NodeJS console. I do not see any message about successful connection.

NodeJS v7.4.0

Can't create DirectLine when WebSocket not already in global scope

Minor issue instantiating an instance of DirectLine:

tobybrad@MININT-IGQ5M5R:~/src/alexa-bridge$ cat test.js

var directLine = require('botframework-directlinejs');
var connector = new directLine.DirectLine({secret:"topsecret"});

tobybrad@MININT-IGQ5M5R:~/src/alexa-bridge$ node test.js
/mnt/c/src/alexa-bridge/node_modules/botframework-directlinejs/built/directLine.js:69
        this.activity$ = this.webSocket && WebSocket !== undefined
                                           ^

ReferenceError: WebSocket is not defined
    at new DirectLine (/mnt/c/src/alexa-bridge/node_modules/botframework-directlinejs/built/directLine.js:69:44)
...

You probably wanted:

(typeof WebSocket != 'undefined' && WebSocket)

PR forthcoming..

Jest Mocking _botframeworkDirectlinejs.DirectLine is not a constructor

I'm trying to mock this library. I get this error:

TypeError: _botframeworkDirectlinejs.DirectLine is not a constructor                                                                                                                                                                                                   > 1 | import { DirectLine } from "botframework-directlinejs";

This is my mock in __mocks__/botframework-directlinejs.js:

jest.mock("botframework-directlinejs", () => jest.fn().mockImplementation(() => ({})));

Any ideas how I can mock out this module in Jest?

RefreshToken loop should be run even if user user provides streamUrl

According to the following snippet here, the refreshTokenLoop function is only run if the streamUrl doesn't exist.

if (this.token && this.streamUrl) {
    this.connectionStatus$.next(ConnectionStatus.Online);
     return Observable.of(connectionStatus);
} else {
     return this.startConversation().do(conversation => {
         this.conversationId = conversation.conversationId;
         this.token = this.secret || conversation.token;
         this.streamUrl = conversation.streamUrl;
         this.referenceGrammarId = conversation.referenceGrammarId;
          if (!this.secret)
               this.refreshTokenLoop();
          this.connectionStatus$.next(ConnectionStatus.Online);
}

This is valid when starting a new conversation but when the user is connecting to an existing conversation, a streamUrl is returned while requesting a directline token.

As of now, we'll need to request a directline token for a conversation on the backend, ignore the streamUrl and only send token and conversationId to directline constructor. This would internally call same URL as that of backend to obtain the streamUrl leading to redundant calls.

GET https://directline.botframework.com/v3/directline/conversations/abc123?watermark=0000a-42

Crash from observableWebSocket broken connection check.

https://github.com/Microsoft/BotFramework-DirectLineJS/blob/af5afc7e9dd4cb7ef5f50ae3ee9881f939556467/src/directLine.ts#L618

This line crashes the entire AsyncScheduler. In my case this is problematic because I have multiple directline connections running at the same time. A crash here kills my entire program.

I use directline in typescript with [email protected], [email protected] and [email protected]. I am using connection via secret, unique user ID per directline instance (in outgoing activities' from field), subscribe to activity$ and connectionStatus$ and properly finalize
subscriptions with unsubscribe from Rx and the end() method from directline.

This is the stacktrace I get when running. It is inconsistently occurring, seemingly linked to the runtime of the entire program -- the exception occures more frequently as runtimes get longer.

.../node_modules/rxjs/scheduler/AsyncScheduler.js:45
            throw error;
            ^

Error: WebSocket is not open: readyState 2 (CLOSING)
    at WebSocket.send (.../node_modules/ws/lib/websocket.js:371:19)
    at SafeSubscriber._next (.../node_modules/botframework-directlinejs/built/directLine.js:356:100)
    at SafeSubscriber.__tryOrUnsub (.../node_modules/rxjs/Subscriber.js:239:16)
    at SafeSubscriber.next (.../node_modules/rxjs/Subscriber.js:186:22)
    at Subscriber._next (.../node_modules/rxjs/Subscriber.js:126:26)
    at Subscriber.next (.../node_modules/rxjs/Subscriber.js:90:18)
    at AsyncAction.IntervalObservable.dispatch (.../node_modules/rxjs/observable/IntervalObservable.js:70:20)
    at AsyncAction._execute (.../node_modules/rxjs/scheduler/AsyncAction.js:111:18)
    at AsyncAction.execute (.../node_modules/rxjs/scheduler/AsyncAction.js:86:26)
    at AsyncScheduler.flush (.../node_modules/rxjs/scheduler/AsyncScheduler.js:36:32)

$activity stream stops after connectionStatus$ emits 'ExpiredToken'.

Hi,
I am trying to handle the ExpiredToken event as detailed here: microsoft/BotFramework-WebChat#372 (comment).

However after the ExpiredToken event is emitted the $activity stream completes and it is no longer possible to receive any messages.

Looking at the code it seems that the error thrown here https://github.com/Microsoft/BotFramework-DirectLineJS/blob/1f5e00d76018cff27bbeef2d1d5dcbce946420aa/src/directLine.ts#L384 ends up in this catch https://github.com/Microsoft/BotFramework-DirectLineJS/blob/1f5e00d76018cff27bbeef2d1d5dcbce946420aa/src/directLine.ts#L617 wich results in the completion of the polling stream.

When using websockets the stream instead completes with 'Error: expired token'.

WebChat using direct line unable to refresh token or reconnect

Hi,
on our project we're using webchat that is connecting to our bot through direct line without web sockets so we are able to restore chat history. After 15-30 minutes of inactivity when user tries to send message it immediately shows couldn't sent. I've tried to use reconnect API however looking into source code it seems that it's working only with web sockets enabled. I've tried to implement following options but neither of them is working and token always expires resulting on 403 error on token refresh.

var botConnection = new BotChat.DirectLine({ 
     secret: model.directLineSecret, 
     webSocket: false, 
     conversationId: getPersistedConversationId(), 
     domain: model.chatUrl 
}); 

BotChat.App({ 
     botConnection: botConnection, 
     user: { 
          id: model.userId, 
          name: model.userName, 
     }, 
}, 
window.document.getElementById('chat')); 

botConnection.connectionStatus$ 
.subscribe(function(status){ 
     handleConnection(status) 
}); 

//try to refresh token every 50 seconds
window.setInterval(function (){ 
  console.log("About to refresh token"); 
  $.ajax({ 
     method: "POST", 
     url: "https://directline.botframework.com/v3/directline/tokens/refresh", 
     headers: { 
       "Authorization" : "Bearer " + botConnection.token 
     } 
     }).done(function(result){  
          botConnection = new BotChat.DirectLine({  
               token: result.token,  
               conversationId: getPersistedConversationId(), 
               webSocket: false, 
               domain: model.chatUrl 
          }); 

     BotChat.App({ 
     botConnection: botConnection, 
     user: { 
               id: model.userId, 
               name: model.userName, 
          }, 
     }, 
     window.document.getElementById('chat')); 

     botConnection.connectionStatus$
          .subscribe(function(status){ 
               handleConnection(status) 
          }); 
     }); 
},50000); 

function handleConnection(connectionStatus){ 
     switch(connectionStatus){ 
     case 0: 
          console.log("Uninitialized"); 
          break; 
     case 1: 
          console.log("Connecting"); 
          break; 
     case 2: 
          console.log("Online")
          saveConversationId(botConnection.conversationId);  
          break; 
     case 3: 
          console.log("ExpiredToken"); 
          botConnection = new BotChat.DirectLine({ 
               secret: model.directLineSecret, 
               webSocket: false, 
               conversationId: getPersistedConversationId(), 
               domain: model.chatUrl 
          }); 

          BotChat.App({ 
               botConnection: botConnection, 
               user: { 
                    id: model.userId, 
                    name: model.userName, 
               }, 
          }, 
          window.document.getElementById('chat')); 

          botConnection.connectionStatus$ 
          .subscribe(function(status){ 
               handleConnection(status) 
          });  
          break; 
     case 4: 
          console.log("FailedToConnect"); 
          break; 
     case 5: 
          console.log("Ended"); 
          break; 
     } 
}

Token refresh does not throw any error but after mentioned interval Expired token is triggered and even if new connection is created it fires request to refresh endpoint resulting 403 error - this method can be used only with existing token.

I've looked at the issue microsoft/BotFramework-WebChat#372

and used c# code to return new conversation object and reconnected, after that message seems to be sent even after timeout however no response from the bot is displayed.

Can anyone point me to the right direction on how to properly manage token of direct line?

ExpiredToken errors should be thrown when calling postActivity

From the following snippet, the ExpiredToken error is resolved as success.

private catchExpiredToken(error: any) {
        return error === errorExpiredToken
        ? Observable.of("retry")
        : Observable.throw(error);
    }

I have the following snippet in my code and the catch block is never reached.

try {
    const messageId = await this.pivbotService.directLine
        .postActivity({
            from: this.from,
            type: 'message',
            text: message
        })
        .toPromise();
    console.log(messageId);
    // Logs text "retry"
} catch (err) {
    // Never reached
    console.error(err);
}

Shouldn't catchExpiredToken method throw error in any case?

502(Bad Gateway) when using HTTP-GET

Hi,

I am building a very simple interactive BOT using only HTTP-GET and plain JS.
Sometimes I have the BOT do time intensive processing and takes 40s to reply back. In such cases I get the below responses. I get the same error even after 40s has elapsed.

Is there a way I can increase this time interval or other ways to avoid this error?

I had gone through this git-issue, but I am not sure how I can implement a similar one for HTTP-GET
microsoft/BotFramework-WebChat#142

Reference:
https://docs.microsoft.com/en-us/bot-framework/rest-api/bot-framework-rest-direct-line-3-0-receive-activities#timing-considerations

What I send -->

{"type": "message","from": {"id": "user1"},"text": "my customer id is a123"}
POST https://directline.botframework.com/v3/directline/conversations/7vBAPXeoH0aCQFPcF5L9bK/activities

What I get in response -->

502 (Bad Gateway)
{
"error": {
"code": "ServiceError",
"message": "Failed to send activity: bot returned an error"
}
}

sending message to BOT

//send token and message
function sendMessage() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
	  

    if (this.readyState == 4 && this.status == 200) {
  
	  var responseObj = JSON.parse(this.responseText);
	  convMsgID =responseObj.id;
	  latestUserMessage = document.getElementById('textToSend').value;
	  showUserMessage();
	  document.getElementById('textToSend').value = "";
	  getReply();
    }
	else{
	  console.log("error :"+ this.responseText);
	}
	
  };
  var postUrl = "https://directline.botframework.com/v3/directline/conversations/" + convID + "/activities";
  xhttp.open("POST", postUrl, true);
  
  var authToken="Bearer " + convToken;
  xhttp.setRequestHeader("Authorization", authToken);
  xhttp.setRequestHeader("Content-Type", "application/json");
  
  
  
  var messageBody = '{"type": "message","from": {"id": "user1"},"text": "';
      messageBody =  messageBody + document.getElementById("textToSend").value;
	  messageBody = messageBody + '"}';
  
  console.log("messageBody"+ messageBody);
  xhttp.send(messageBody);
  document.getElementById("send-icon").src="./send.png";
  
}

Receive message from BOT

function getReply() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
	  
	  
	
    if (this.readyState == 4 && this.status == 200) {
      
	  var responseObj = JSON.parse(this.responseText);
	  console.log("length" + (responseObj.activities.length -1));
	  console.log("response :"+ responseObj.activities[responseObj.activities.length -1].text);
	  latestBotMessage = responseObj.activities[responseObj.activities.length - 1].text
	  
	  
	  showBotMessage();
    }
	else{
		console.log("response"+ this.responseText);
	}
	
  };
  var postUrl = "https://directline.botframework.com/v3/directline/conversations/" + convID + "/activities";

  xhttp.open("GET", postUrl, true);
  
  var authToken="Bearer " + convToken;
  xhttp.setRequestHeader("Authorization", authToken);
 xhttp.send();
  
}

LocalTimestamp field is empty

Perhaps the control should populate the LocalTimestamp field when sending activities. The field is designed to be supplied by the client and can contain timezone/offset information if available.

Subscriptions to activity$ do not work after user input

I'm not sure why this has to do with user input, however my investigation shows that the method (polling or webjob) is clearly executing and calling through to next() in react. However it's almost as if the subscription is being unsubscribed. If I bypass RxJS and poll directline manually of course it works fine.

On the client side I have the following code that will generate data on the initial navigation (conversation start). But once I send a command to the bot or if any activities come in after typing, this subscription does not execute.

ASK: Is there any known issue with RxJS and the ajax observable? Or (more likely), am I doing something wrong?

 <script type='text/javascript'>
            var directLineConnection = new BotChat.DirectLine({ secret: '@ViewBag.DirectLine' });

            BotChat.App({
                directLine: directLineConnection,
                user: { id: 'userid' },
                bot: { id: 'answer-staging' },
                resize: 'detect'
            }, document.getElementById('botDiv'));

            directLineConnection.activity$
                .subscribe(function (activity) {
                    console.log(' received with value: ' + activity.value);
                });
    </script>

Constantly getting Failed to send activity: bot timed out

Guys, I am in troubles. I am constantly getting
{
"error": {
"code": "BotError",
"message": "Failed to send activity: bot timed out"
},
"httpStatusCode": 504
}

and I am not sure why. Checking AI from web app, shows that the call is actually made and taking around 2 sec. to process. The back end also reply some message back and web chat client shows it. However, all activities I am sending, in Chrome showing with 504 error.

Bot Framework shows this error in DirectLine Chanel:
There was an error sending this message to your bot: HTTP status code GatewayTimeout

Any idea guys? Should I somehow reply something special to those activities?

Thanks
Nick

Question | Directline message Ids

Same question: microsoft/botframework-sdk#2170, just hoping for a quick response.

So we've got a web interface which uses https://www.npmjs.com/package/botframework-directlinejs (works really well) and we use the fact that messages returned via directline (bot and user msgs) have a messageId of the format "G0SRNz7iwTiLehjQsXk3tI|0000001", "G0SRNz7iwTiLehjQsXk3tI|0000002" etc to sort on the sent and received ones on our FE.

However... Messages (Activities) in the bot code itself have a messageId of the format of a GUID. Even if you got to that point in code from a request via directline.

So my question is: does directline have some sort of interception and enrichment of the messages (ids) on your side before returning to the user? Why then do these messages not have the same ID in our bot code itself?

Reason I'm asking is, we broadcast messages to a separate 'admin-console' via signalR. But the message has a GUID id. But the message had a directline id on the webchat side.

You can imagine how this mucks with sorting messages in their correct order. Especially as the timestamp on the messages is not reliable - messages are frequently in the wrong order.

Any feedback very much appreciated.

Angular 2 Implementation

Any guidance or an example of using the BotFramework-DirectLineJs for 3,0 with Angular 2

Cheers

AudioCard does not support Images

According to the documentation, the AudioCard supports an Image.

However, when connecting to a bot that uses AudioCards with images, they are not displayed. Looking at the code, the AudioCard does not support this feature.

Could it be implemented, please?

Thank you

Loading no history on startup

We are interested on a feature that would allow us to load basically no history when the user returns to the chat, but keeping it on the same conversation for audit/log purposes.

This will allow us to load the chat much faster but at the same time recover the whole conversation history if needed.

Any idea or suggestion of how we can achieve this?

DirectLine not working with FireFox

In the chat UI that I made, I connect with directline as indicated in the docs (var directLine = new DirectLine.DirectLine({secret: directLineSecret});) it works perfectly with Chrome and Microsoft Edge, but with firefox it just doesn't connect. When typing in the console the command, it creates a directLine object without a conversation id; therefore, everything that I type doesn't get a response.
capture

Also my listener when posting activities to directLine prints in console a message with the id on success, and the error otherwise, but none of the listeners is executing.

function sendDirectLineActivity(activity) { var post = directLine.postActivity(activity); post.subscribe( id => console.log("Success, id:", id), error => console.log("Error, error:", error)); console.log("Send activity: ", activity.text); return post; }

pollingGetActivity's Authorization header is "Bearer undefined" when directLine's secret is empty string and webSocket is false

Hi Sirs,
I use BotFramework-WebChat "samples/fullwindow/index.html" and directLine's secret set to empty string, webSocket set to false. code are below,

directLine: {
        secret: '',
      token: params['t'],
        domain: params['domain'],
        webSocket: false
      }

I found polling request's Authorization header is always "Bearer undefined", because checkConnection method will set directLine's token to undefined (conversation.token is undefined) when secret is empty.
May not set Authorization header when secret and token are empty string?
Thanks.

Allow users to reference conversationId property

conversationId property is currently set to private here. Users cannot reconnect to an existing conversation or handle ConnectionStatus.ExpiredToken state.

Change the access modifier to public to allow users to store conversationId externally in a db/localstorage.

This can be overcome by starting the conversation ourselves on the backend and passing streamUrl, token and conversationId to Directline constructor, but as of this code, the refreshToken loop will not be run by directline and users have to manually handle it.

Stuck connecting

Hi, I enabled Direct Line as a channel on the portal. Im using the npm botframework-directlinejs package to connect using the generated secret. The connection status gets stuck on Connecting.

Im taking the basic code from the README file in this repo, nothing added to it (NOTE cli.secret contains the generated secret):

var directLine = new DirectLine({
  secret: cli.secret
});

directLine.connectionStatus$
  .subscribe(connectionStatus => {
    switch(connectionStatus) {
      case ConnectionStatus.Uninitialized:    // the status when the DirectLine object is first created/constructed
        logger.debug('Status: Uninitialized');
        break;
      case ConnectionStatus.Connecting:       // currently trying to connect to the conversation
        logger.debug('Status: Connecting');
        break;
      case ConnectionStatus.Online:           // successfully connected to the converstaion. Connection is healthy so far as we know.
        logger.debug('Status: Online');
        postActivity();
        break;
      case ConnectionStatus.ExpiredToken:     // last operation errored out with an expired token. Your app should supply a new one.
        logger.debug('Status: ExpiredToken');
        break;
      case ConnectionStatus.FailedToConnect:  // the initial attempt to connect to the conversation failed. No recovery possible.
        logger.debug('Status: FailedToConnect');
        break;
      case ConnectionStatus.Ended:
        logger.debug('Status: Ended');
        break;
      default:
        logger.debug('Status: ', connectionStatus);
    }
  });

React Native throws exception on ws.send(null)

Please feel free to close this if you don't think it's an issue.

Using React Native in 20 seconds after the direct line was initialized, the app throws an exception in Android and iOS: "Unsupported data type" which comes from the WebSocket's send function. This happens because in the directline's code it explicitly sends through null to the send function. This works on React in Chrome and Safari. But on the app this always throws an exception.

My temporary solution was to comment out the directline's code where it throws an exception. Also using polling works but I don't want to. Do anyone else have this issue?

Add support for posting ConversationUpdate types

Hello,
Trying to post a event of type ConversationUpdate, but the type is not currently supported.
Can either the validation on the type be removed or the IConversationUpdate interface be implemented.
Thank you,
Douw

Microsoft bot directline api issue returning incorrect response

While calling Directline API from my application, I get only a single entry in the Messages array, the response basically contains only the request text and not the response returned by the BOT.

This behavior happened on 6th June 2017 and lasted for almost 15 hours after which the API started responding with both the request text and response message from the BOT.

Would anyone know why this issue occurred intermittently and how was it solved

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.