Your implementation is correct and, as you noted, it is running. It isn't doing anything, however, because you haven't setup any way for a user to interact with it. For instance, a field to enter a message that, on submit, then posts the message as an activity using directLine.postActivity()
. As you appear to have a running bot, you just need to figure out this latter part.
I've included a sample I created that demonstrates this, below. There's nothing pretty about it as it was thrown together to show the "how" in somewhat simple terms.
In short, it:
- Retrieves the DirectLine token from a token server I run locally (as secrets shouldn't be in HTML for security reasons since anyone can access them)
- Supports displaying simple chat messages from the bot and the user
- Logs connection status and incoming activities to the developer's console
It does not support activities that include any attachments, speech, etc. Support for these could be added, but for a simple demo I chose not to.
Also, note that in the subscribeToActivities()
function you will need to replace the id in each case statement with your bot's handle. If you don't remember what this is, you can find it in your bot's profile in your bot's channel registration in Azure. I'm not sure why I did it this way, but I did and for the demo...well, it works well enough.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="favicon.ico" type="image/x-icon" />
<title>Custom Chat Using Direct Line</title>
<script crossorigin="anonymous" src="https://unpkg.com/@babel/standalone@7.8.7/babel.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/botframework-directlinejs@0.15.1/dist/directline.js"></script>
</head>
<body>
<h2>Custom Chat Using Direct Line</h2>
<div class="input-container">
<input type="text" class="input-box" name="input-box" value="Hi">
<button type="button" class="input-button">Send</button>
</div>
<div class="response-container">
</div>
<script type="text/babel" data-presets="es2015,react,stage-3">
( async function () {
const { ConnectionStatus, DirectLine } = window.DirectLine;
const renderStatus = {
DISPLAY_ACTIVITIES: 'display',
HIDE_ACTIVITIES: 'hide',
RESET_VIEW: 'reset',
MAINTAIN_VIEW: 'maintain'
}
let previousWatermark = 0;
let currentWatermark = 0;
let displayStatus = renderStatus.DISPLAY_ACTIVITIES;
let viewStatus = renderStatus.MAINTAIN_VIEW;
let responseHistory;
// Custom 'token' server retrieves a Direct Line token
// Server stores the Direct Line secret exchanging it for a token when requested
const res = await fetch( 'http://localhost:3500/directline/conversations', { method: 'POST' } );
const { token } = await res.json();
var directLine = new DirectLine( {
token: token
} )
// Function posts activity to Direct Line, when called
const postActivity = ( dl, text ) => {
dl.postActivity(
{
from: { id: 'dl_abc123', name: 'JohnDoe' }, // required (from.name is optional)
type: 'message',
text: `${ text }`
}
)
// As all message activities are logged below, there is no need to log the posted activity
.subscribe(
id => id,
error => console.log( "Error posting activity", error )
);
}
// Posts user message on button click
const inputButton = document.querySelector( '.input-button' );
const inputBox = document.querySelector( '.input-box' );
inputButton.addEventListener( 'click', ( e ) => {
e.preventDefault();
const text = inputBox.value;
postActivity( directLine, text );
} );
inputBox.onkeyup = ( e ) => {
const keyCode = e ? ( e.which ? e.which : e.keyCode ) : event.keyCode;
if ( keyCode === 13 ) {
const text = inputBox.value;
postActivity( directLine, text );
}
};
// Updates UI with all response activity
let responseContainer = document.querySelector( '.response-container' );
const subscribeToActivities = (dl) => {
dl.activity$
// Filters activities to show only 'message' types. 'Event' types, etc., are not captured
.filter( activity => {
console.log('INCOMING ACTIVITY ', activity)
return activity.type === 'message';
} )
.subscribe(
activity => {
const text = activity.text;
// Checks if the activity includes any attachments
if (!activity.attachments) {
const id = activity.from.id;
currentWatermark = Number(dl.watermark);
if ( viewStatus === renderStatus.RESET_VIEW && currentWatermark <= previousWatermark && responseHistory.length > 0) {
displayStatus = renderStatus.HIDE_ACTIVITIES;
viewStatus = renderStatus.MAINTAIN_VIEW;
}
// Displays activities coming from the bot
else if ( displayStatus === renderStatus.DISPLAY_ACTIVITIES && currentWatermark >= previousWatermark ) {
switch ( id ) {
case '<YOUR BOT'S HANDLE>':
responseContainer.innerHTML += `<ul class="chat-list"><li>From Bot: ${ text } </li></ul>`;
displayStatus = renderStatus.HIDE_ACTIVITIES;
viewStatus = renderStatus.MAINTAIN_VIEW;
break;
}
}
// Displays activities coming from the user
else if ( displayStatus === renderStatus.HIDE_ACTIVITIES && currentWatermark >= previousWatermark ) {
switch ( id ) {
case '<YOUR BOT'S HANDLE>':
break;
default:
responseContainer.innerHTML += `<ul class="chat-list"><li>From User: ${ text } </li></ul>`;
displayStatus = renderStatus.DISPLAY_ACTIVITIES;
viewStatus = renderStatus.MAINTAIN_VIEW;
}
}
}
else {
responseContainer.innerHTML += `<ul class="chat-list"><li>From Bot: Client received unsuppported attachment type </li></ul>`;
}
}
);
}
subscribeToActivities(directLine);
// Logs the connection status
directLine.connectionStatus$
.subscribe( async connectionStatus => {
switch ( connectionStatus ) {
case ConnectionStatus.Uninitialized:
console.log( 'CONNECTION_STATUS => UNINITIALIZED ', directLine );
break;
case ConnectionStatus.Connecting:
console.log( 'CONNECTION_STATUS => CONNECTING ', directLine );
break;
case ConnectionStatus.Online:
console.log( 'CONNECTION_STATUS => ONLINE ', directLine );
break;
case ConnectionStatus.ExpiredToken:
console.log( 'CONNECTION_STATUS => EXPIRED TOKEN ', directLine );
break;
case ConnectionStatus.FailedToConnect:
console.log( 'CONNECTION_STATUS => FAILED TO CONNECT ', directLine );
break;
case ConnectionStatus.Ended:
console.log( 'CONNECTION_STATUS => ENDED ', directLine );
break;
}
} );
} )()
</script>
</body>
</html>
Hope of help!