Team,
I have developed a bot using bot framework SDK4.
I am using Directline channel to communicate with my bot.
My requirement is based on channeldata on 'requestWelcomeDialog' message I have to show welcome message.
Code from my bot client:
BotChat.App({
botConnection: botConnection,
user: userOption,
bot: { id: model.botId, name: model.botName },
resize: 'window',
speechOptions: speechOptions,
locale: 'en',
sendTypingIndicator: true,
}, document.getElementById('BotChatElement'));
PostBotConfiguration();
botConnection
.postActivity({
from: user,
name: 'requestWelcomeDialog',
type: 'event',
value: { 'BotType': 'abcd' }
})
.subscribe(function (id) {
setWCScreenChatPosition();
model.botRender = true;
console.log('"trigger requestWelcomeDialog" sent');
});
In the above code i am sending BotType as 'abcd'.
I am trying to read this value from my bot.
My code in bot.
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
Utility util = new Utility();
try
{
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
BotChannelData cdata = new BotChannelData();
turnContext.Activity.TryGetChannelData(out cdata);
}
}
}
catch
{
}
}
In this I am always getting null reference exception.
May I know what I am missing in this?
The first problem is that you're using Bot Chat. Bot Chat is Web Chat v3 and it's deprecated. You should be using Web Chat v4 according to the instructions in the repo.
The second problem is that you're trying to respond to a custom event using OnMembersAddedAsync which is only triggered by conversation update activities. You can see how to send and respond to welcome events by following the instructions in this issue and this sample. The C# equivalent would look like this:
if (turnContext.Activity.Name == "webchat/join")
{
await turnContext.SendActivityAsync("Back Channel Welcome Message!");
}
If you use Direct Line channel, you should use Web Chat v4. Because Web Chat v3 is now deprecated.
You could get the sending welcome message code from this official sample.
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Web Chat: Send welcome event</title>
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<style>
html, body { height: 100% }
body { margin: 0 }
#webchat {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="webchat"></div>
<script>
(async function () {
const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
const { token } = await res.json();
const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join',
value: { language: window.navigator.language }
}
});
}
return next(action);
});
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
</script>
</body>
</html>
Related
I am trying to run a WebApp which allows files sharing.
After few google search, I found Web Share API like the standard to do so.
According to the documentation it should works like this using plain JS
This is the code for html page
<p><button>Share MDN!</button></p>
<p class="result"></p>
The code to share all sort "textbased" metadata:
let shareData = {
title: 'MDN',
text: 'Learn web development on MDN!',
url: 'https://developer.mozilla.org',
}
const resultPara = document.querySelector('.result');
if (!navigator.canShare) {
resultPara.textContent = 'navigator.canShare() not supported.';
}
else if (navigator.canShare(shareData)) {
resultPara.textContent = 'navigator.canShare() supported. We can use navigator.share() to send the data.';
} else {
resultPara.textContent = 'Specified data cannot be shared.';
}
The code above works fine, the trouble happens when I try to share files.
According to the documentation it should works like this:
// filesArray is an array of files we want to share (audios, images, videos, pdf)
if (navigator.canShare && navigator.canShare({ files: filesArray })) {
navigator.share({
files: filesArray,
title: 'Pictures',
text: 'Our Pictures.',
})
.then(() => console.log('Share was successful.'))
.catch((error) => console.log('Sharing failed', error));
} else {
console.log(`Your system doesn't support sharing files.`);
}
I started my code from this example and I never success to share a file.
My actual code using React and Typescript looks like this:
//some react code here
const shareNow = async () => {
let imageResponse = await window.fetch('https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png', {mode: "no-cors"});
let imageBuffer = await imageResponse.arrayBuffer();
let fileArray = [new File([imageBuffer], "File Name", {
type: "image/png",
lastModified: Date.now()
})];
if (navigator.canShare && navigator.canShare({ files: filesArray })) {
navigator.share({
files: filesArray
}).then(() => {
console.log('Thanks for sharing!');
})
.catch(console.error);
}
}
//some react code here too
At this point, my typescript compiler yell at me.
Apparently, the navigator object has no method canShare()
I am new to typescript, but I don't understand how and why the navigator could have less attribute since TypeScript is JavaScript superset.
Anyone has an idea on how to solve that except running normal JS ?
Thank you for your time reading this, and I hope to thank you for your answers.
P.S: I also tried a react-component based solution, but all the component I found in open source which wraps Web Share API does not allow file sharing.
Edit
Hey, #DenverCoder9
There is the same use case but using vanilla JS, could anyone try it and tell me what I am doing wrong please ?
<html>
<head>
<title>Sharing Image</title>
<meta charset="UTF-8" />
</head>
<body>
<div className="App">
<img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"/>
<button id="button">Share</button>
</div>
</body>
<script>
async function shareImage(title, imageUrl) {
const image = await fetch(imageUrl, {mode: "no-cors"});
const blob = await image.blob();
const file = new File([blob], title, { type: 'image/png' });
const filesArray = [file];
const shareData = {
files : filesArray
}
// add it to the shareData
const navigator = window.navigator
const canShare = navigator.canShare && navigator.canShare(shareData) //navigator.canShare()navigator.share //navigator.canShare()
if(canShare){
navigator.share(shareData)
.then(() => console.log('Successful share'))
.catch((error) => console.log('Error sharing', error));
}
else {
console.log("cannot share this file in this context")
}
}
document.getElementById('button').onclick = function() {
shareImage("Title", "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png")
};
</script>
</html>
I am running this on safari for mac
This is more of a TypeScript issue than a coding issue. Support for the Web Share API (Level 2) was added in this PR, so you can either update to a version of TypeScript that includes this, or alternatively teach your current TypeScript version the relevant types as follows:
type ShareData = {
title? : string;
text? : string;
url? : string;
files?: ReadonlyArray<File>;
};
interface Navigator
{
share? : (data? : ShareData) => Promise<void>;
canShare?: (data?: ShareData) => boolean;
}
I want to give user possibility to choose his own value of Ether. How to do this?
How efficiently change the network when pressing connect to metamask (for example when user will press this button metamask should change network to binance smart chain (ID 56)
here is my code:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/web3#latest/dist/web3.min.js"></script>
</head>
<body>
<div>
<button class="enableEthereumButton btn">Enable Ethereum</button>
<button class="sendEthButton btn">Send Eth</button>
<div id="status"></div>
</div>
<script type="text/javascript">
// Running on the page, in the browser
if (typeof window.ethereum !== 'undefined') {
console.log('MetaMask is installed!');
}
if (!ethereum || !ethereum.isMetaMask) {
throw new Error('Please install MetaMask.')
}
/*********************************************************/
/* Handle chain (network) and chainChanged, per EIP 1193 */
/*********************************************************/
// Do this:
ethereum.on('chainChanged', (chainId) => {
/* handle the chainId */
});
const ethereumButton = document.querySelector('.enableEthereumButton');
const sendEthButton = document.querySelector('.sendEthButton');
let accounts = [];
//Sending Ethereum to an address
sendEthButton.addEventListener('click', () => {
ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
from: accounts[0],
to: '0x6adress.................',
value: '0x00',
gasPrice: '0x0000001F6EA08600',
gas: '0x0001ADB0',
},
],
})
.then((txHash) => console.log(txHash))
.catch((error) => console.error);
});
ethereumButton.addEventListener('click', () => {
getAccount();
});
async function getAccount() {
accounts = await ethereum.request({ method: 'eth_requestAccounts' });
}
</script>
</body>
</html>
Metamask screenshot
Add an input field for the value and pass it in to your params. This is basic html form + javascript interactions, not specific to web3, so for more info on how to do that I would look here https://www.w3schools.com/html/html_forms.asp
To read the network the user is connecting with in your app you can listen for a chain change event: https://docs.metamask.io/guide/ethereum-provider.html#events
Then if they are not connected to a network your app supports you should show the user a notice.
I'd like to implement Google OneTap in my Angular Universal app (that uses SSR). I'm using Angular 11, and the following script was working before converting the app to Angular Universal:
initGoogleOneTap() {
const domain = window.location.hostname;
google.accounts.id.initialize({
client_id: environment.GOOGLE_OAUTH_CLIENT_KEY,
cancel_on_tap_outside: false,
auto_select: true,
state_cookie_domain: domain,
callback: (response: any) => {
this.oneTapLogIn(response.credential)
.catch( (error) => {
console.error('Error logging in with Google One Tap: ', error);
});
},
});
google.accounts.id.prompt( (notification: any) => {
if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
// try next provider if OneTap is not displayed or skipped
}
});
}
I am using declare var google: any at the top of the script.
The error I am getting is: ERROR ReferenceError: google is not defined
First, load client library by putting this in your index.html
<script src="https://accounts.google.com/gsi/client" async defer></script>
Then, implement your component like this
declare var window: any;
ngOnInit() {
window.google.accounts.id.initialize({
client_id: 'YOUR CLIENT ID',
callback: this.handleGgOneTap.bind(this)
});
window.google.accounts.id.prompt();
}
handleGgOneTap(resp) {
console.log('handleGgOneTap ', resp);
}
I'm trying to integrate the backchannel and getting the values.
https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/15.d.backchannel-send-welcome-event
I also tried this. Get URL Referer and Origin header from Microsoft Bot Framework
I also tried deserializing the values still not able to get the data.
how can i get the language values?
here's my sample code:
var userinfo = {
id: 'user-id',
name: 'user name',
locale: 'es'
};
var botConnection = new BotChat.DirectLine({
token: 'mytoken',
user: userinfo,
locale: 'es'
});
BotChat.App({
botConnection : botConnection,
user: userinfo,
bot: { id: 'bot-id', name: 'bot name' },
}, document.getElementById('botDiv'));
botConnection
.postActivity({
from: userinfo,
name: 'ConversationUpdate',
type: 'event',
value: '',
})
.subscribe(function (id) {
console.log('"trigger ConversationUpdate" sent');
});
The purpose of this I want to pass the locale to my bot from my website.
just like in the emulator.
Thanks!
I would recommend adding the locale to the back channel event's channel data. That way on the bot side you can simply access the locale in the incoming activity without having to deserialize any JSON objects when you receive the event. Note, you can also use text or value in place of channelData. See the code snippets below.
BotChat Back Channel Event
// Send back channel event
botConnection.postActivity({
from: userinfo,
name: 'setLocale',
type: 'event',
channelData: "es"
}).subscribe(id => console.log(id));
Bot - C#
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
...
} else if (turnContext.Activity.Type == "event") {
// Check for `setLocale` events
if (turnContext.Activity.Name == "setLocale") {
await turnContext.SendActivityAsync($"Your locale is set to {turnContext.Activity.ChannelData}");
}
}
else
{
await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected");
}
}
Hope this helps!
I am using the Yelp API (by making requests to it using https://cors-anywhere.herokuapp.com/ as a proxy—because the Yelp API itself isn’t CORS-enabled) to attempt to create an app very similar to a Yelp search for my own practice. I am able to get the response into my browser's console. The response has an array named businesses with the businesses that match the search. I am trying to use .(map) to map through the businesses array. However, I keep getting Cannot read property 'map' of undefined.
The reponse I receive from the Yelp API is below. Thanks
Yelp API Response Image
Also if there is any other tips about javascript that come to mind when looking at my code please share as I am very early into my programming career.
const Yelp = {
getAccessToken: function() {
if(accessToken) {
return new Promise(resolve => resolve(accessToken));
};
return fetch(accessTokenUrl, {
method: 'POST'
}).then(response => response.json()).then(jsonResponse => {
accessToken = jsonResponse.access_token;
})
},
search: function(term,location,sortBy) {
return this.getAccessToken().then(() => {
const yelpRetrieveUrl = `${corsAnywhereUrl}https://api.yelp.com/v3/businesses/search?term=${term}&location=${location}&sort_by=${sortBy}`;
return fetch(yelpRetrieveUrl, {
headers: {Authorization: `Bearer ${accessToken}`}
});
}).then(jsonResponse => {
if(1 > 0){
console.log('true!!!!');
return jsonResponse.businesses.map(business => {
return {
id: business.id,
imageSrc: business.image_url,
name: business.name,
address: business.location.address1,
city: business.location.city,
state: business.location.state,
zipCode: business.location.zip_code,
category: business.categories,
rating: business.rating,
reviewCount: business.review_count
};
});
} else {
console.log('FALSE')
}
})
}
};
fetch(myRequest).then(function(response) {
var contentType = response.headers.get("content-type");
if(contentType && contentType.includes("application/json")) {
return response.json();
}
throw new TypeError("Oops, we haven't got JSON!");
})
.then(function(json) { /* process your JSON further */ })
.catch(function(error) { console.log(error); });
You might search for the jsonResponse#json function to turn your json dataset into a real object that you can process in JavaScript.
And because you asked for it: As you are using Promises, make use of the Promise#Catch function to handle upcoming errors. Don't let the browser handle them, because they can have different behaviors in different browsers.
And probably remove the 1 > 0 check, because it will always be true, but I think this was only for test purposes.
I hope I could help you! I might append the code later since I'm currently on mobile.