I need trigger a specific dialog in ibm-watson conversation, but without ask the user to type something (like a intent). I need to use botkit to init a specific dialog. That's is possible? I'm looking for in all possible documentation and links at Google, but not successfully :/
Sending initial empty message triggers welcome event in the dialog.
To make it do something different, you could set some variable in the context and add condition for that variable to welcome branch in the dialog.
This is how I implemented it in my bot:
function handleHelloEvent(bot, message) {
message.type = 'welcome';
const contextDelta: any = {};
if (message.intent) {
contextDelta.initialIntent = message.intent;
}
//more fields here
watsonMiddleware.sendToWatsonAsync(bot, message, contextDelta).catch((error) => {
message.watsonError = error;
}).then(() => {
//this is the same function which handles message_received events
return handleWatsonResponse(bot, message);
});
}
function handleWatsonResponse(bot, message) {
bot.reply(message, message.watsonData.output.text.join('\n'));
}
controller.on('hello', handleHelloEvent);
controller.on('message_received', handleWatsonResponse);
hello event is specific to webchat/botkit anywhere, you may need to handle different events for different platfoms.
A similar example of code handling welcome event: https://github.com/watson-developer-cloud/botkit-middleware/#dynamic-workspace
(I wrote that one too, so it is a bit too similar).
Dialog example:
Related
I'm completely new to SalesForce and have inherited a report that's not working. Please excuse any incorrect terminology, since I'm learning about all this as I go. The report has three prompts: states, years, and members. All dropdowns are supposed to populate with data returned from functions in an APEX class. State, which populates from a picklist, and years, which is populated with a loop, work fine. Members, which populates from a SQL query, returns nothing. If I run the report without any prompts selected (which should return an unfiltered list of results from a SQL query), it also returns nothing. Both of the SQL queries return data when I execute them directly in the query editor in the developer console, but they return nothing when called from the APEX functions.
Here's the initialization code from the Lightning controller:
doInit: function (component, event, helper) {
var action = component.get('c.getTrcAccounts');
action.setCallback(this, function (response) {
var state = response.getState();
if (state === 'SUCCESS' && component.isValid()) {
component.set('v.trcAccList', response.getReturnValue());
}
helper.getLocationState(component, event);
helper.getYear(component, event);
});
$A.enqueueAction(action);
},
Here are the two helper functions referenced in that code:
getLocationState: function (component, event) {
var action = component.get('c.getLocationState');
action.setCallback(this, function (response) {
var state = response.getState();
if (state === 'SUCCESS') {
component.set('v.LocationStateList', response.getReturnValue());
}
});
$A.enqueueAction(action);
},
getYear: function (component, event) {
var action = component.get('c.yearsOptions');
action.setCallback(this, function (response) {
var state = response.getState();
if (state === 'SUCCESS') {
component.set('v.LocationYearList', response.getReturnValue());
}
});
$A.enqueueAction(action);
}
Here is the code from the APEX class that returns the data for those three prompts:
Global class DataTableLocations {
#AuraEnabled
Global static List<TRC_Account__c> getTrcAccounts(){
set<string> trcAccountSet = new set<string>();
List<TRC_Account__c> traccList = new List<TRC_Account__c>();
for(TRC_Account__c trcacc : [SELECT Id, Name from TRC_Account__c WHERE TRC_Member__c = True order by Name limit 50000]){
if(!trcAccountSet.contains(trcacc.Name)){
trcAccountSet.add(trcacc.Name);
traccList.add(trcacc);
}
}
if(traccList.size()>0){
return traccList;
}
else{
return null;
}
}
#AuraEnabled
Global static List<string> getLocationState(){
List<string> options = new List<string>();
//options.add(new SelectOption('SelectAll', 'Select All'));
for( Schema.PicklistEntry f : Location__c.Physical_Address_State__c.getDescribe().getPicklistValues()) {
options.add(f.getValue());
}
return options;
}
#AuraEnabled
Global static List<string> yearsOptions() {
List<string> options = new List<string>();
date OldDate= date.today().addYears(-18);
integer oldyear=OldDate.year();
for( integer i=0; i<19 ;i++) {
options.add(string.valueOf(oldyear));
oldyear++;
}
return options;
}
}
If I run SELECT Id, Name from TRC_Account__c WHERE TRC_Member__c = True order by Name limit 50000 directly in the query editor window in the developer console, I get 7 results. However, if I output the response.getReturnValue() for getTrcAccounts in the doInit function, it's null.
Any help is greatly appreciated, as we're in a bit of a time crunch in conjunction with a site redesign. I'm told these reports were working at one point, but no one knows when they stopped working, and we inherited this code from a different company that did the original development. Thank you!
UPDATE:
In case it helps, this is the code in the lightning app that I think is used on the public page:
<aura:application extends="ltng:outApp" access="GLOBAL" implements="ltng:allowGuestAccess">
<aura:dependency resource="c:SearchBinReceiptsByYear"/>
</aura:application>
Edit
Right, it's a public page, it's called "Salesforce Sites". It's exposed to whole world without having to log in. These have special security in place because most of the time you don't want to expose data like that. At best you'd display contact us form, maybe some documents to download, product catalog... It's all very locked down, default is to ban everything and then admin decides what's allowed. It's bit unusual to have a Visualforce page + Aura component but ok, it happens.
You (and any other internal user) can see the results if you'd access this page from within salesforce. Something like https://mydomain.my.salesforce.com/apex/SearchBinReceiptsByYear and for you the page will work fine, "just" not outside of salesforce.
When exposed like that on the web - there's no logged in user. There's special "[Site Name] Guest User", you can see them if you search "Sites" in Setup. It has a special profile, also with [Site Name] in it. And nasty thing is - it doesn't show on the list of Users or Profiles.
Your code broke when Salesforce (auto)activated a critical update. Probably this one: https://releasenotes.docs.salesforce.com/en-us/spring20/release-notes/rn_networks_secure_perms_guests.htm There are some good resources on the net if you Google "Secure Object Permissions for Guest Users", for example https://katiekodes.com/salesforce-spring-20-guest-user/
Ask your system administrator colleague or read up a bit about sharing rules.
You'll have to go to Setup -> Sharing Rules. There's a checkbox that caused your stuff to break and you can't untick it.
Scroll down to your TRC Account object and hit "New". You'll need to create something like this, but with your criteria (TRC Member equals true)
Save, wait a bit (it might take a while to recalculate the sharing, you'll get an email) and try the page.
If it still doesn't work you'll have to check the Guest user's profile, it might need permissions to Read TRC Accounts and their Name field.
If it's Salesforce Sites - try this to find it: https://help.salesforce.com/articleView?id=000334554&type=1&mode=1
If it's a Customer Portal, Community, Digital Experience (they renamed the product few times) - try with https://help.salesforce.com/articleView?id=sf.rss_config_guest_user_profile.htm&type=5
Original answer
It looks like it's running OK because accounts (members?) are fetched first and in that fetch's callback (what to do when data comes back from server) you have helper.getLocationState, helper.getYear. And you wrote that these populate OK. It's not the best performance code but it should get the job done.
In no specific order...
Does the whole thing work OK for sysadmins? Or is it broken for everybody? If it works for sysadmins it might be something to do with sharing, your sysadmin should know (Setup -> Sharing settings is where you control who can see what. Maybe "mortals" are not allowed to see any data? Typically sysadmins bypass it. As a quick & dirty test you can modify the class definition to global without sharing class DataTableLocations but it's a really ugly hack.
What happens if you open DeveloperConsole (upper right corner) while running this component, do you see any errors in the logs? What happens if in the console you go Debug -> Open ExecuteAnonymous and run this piece of code:
System.debug(DataTableLocations.getTrcAccounts());
Does it return something? Throw error?
You can go to Setup -> Debug Mode, tick the checkbox next to your user and save. This slows the system down a bit but lets you debug the javascript better. You can then sprinkle some debugger; or console.log statements in the source code and view what happens in your browser's console (Ctrl+Shift+J in Chrome, Ctrl+Shift+I in firefox). For example
action.setCallback(this, function (response) {
var state = response.getState();
debugger;
console.log(state);
console.log(component.isValid());
console.table(response.getReturnValue());
if (state === 'SUCCESS' && component.isValid()) {
component.set('v.trcAccList', response.getReturnValue());
}
console.log(component.get('v.trcAccList'));
debugger;
helper.getLocationState(component, event);
helper.getYear(component, event);
});
How's the trcAccList variable actually used in the "cmp" file, in the HTML-like file? Maybe it's being set all right and contains 7 records but it's not displayed right?
I've been having some issues with how to best handle the device data of discovered peripherals and wanted to see if anyone can shine some light on this.
I am able to scan for devices perfectly fine, and my "success" callback works as well. What I want to do is create a list that displays the found devices and connects to the one that is selected. I have no problem with creating the list with ng repeat, however I am unsure as to how to proceed with the data that is returned by the success function. How can I go about saving each peripheral into an array so that I can access each individual peripheral's name, id , rssi, etc? I have tried something along the lines of creating an array outside the function to store the peripherals in, and this works perfectly fine inside the starScanning function, however I can't push them into an array from inside the success callback function. If I just store the scan results into an array from inside the startScanning function, is that sufficient?
startScanning = function() {
this.ble.startScan([], this.success).subscribe(device => {
console.log(JSON.stringify(device.name));
});
this.isScanning = true;
this.presentLoading();
setTimeout(() => {
this.ble.stopScan().then(() => {
console.log("Scanning has stopped");
this.isScanning = false;
});
}, 3000);
}
success = function(peripheral) {
console.log("Success Callback called");
console.log(peripheral.rssi);
}
You can handle data returned from the scan in the subscribe callback (the startScan method is supposed to only take an array of services as the parameters according to the docs:
this.scanSubscription = this.ble.startScan([])
.subscribe( device => {
// device will have an id property that you can use to connect
this.devices.push(device); // devices is an array on your component
});
Now, you can use *ngFor to loop over the devices array and attach a click handler that passes the device's id:
<ion-list>
<ion-item *ngFor="let device of devices" (click)="connectToDevice(device.id)">{{device.id}}</ion-item>
</ion-list>
The connectToDevice() takes a device id and connects:
connectToDevice(id: string) {
this.connectSubscription = this.ble.connect(id)
.subscribe((data) => {
console.log('Successfully connected!');
});
}
A couple of notes:
It's considered good practice to store subscriptions as properties of your page/component so that you can call unsubscribe()on them when a page/component is destroyed to prevent memory leaks
I'd recommend checking out the startScanWithOptions function since you can pass the option reportDuplicates with a value of false to ignore duplicate devices
Check out my Github repo (especially the page file bluetooth.ts) for a working demo app (this uses the startScanWithOptions function I mentioned above, but the functionality is the same)
in the doc it's written that you can sends event to all connected users by using the following server side code for an update event
function backandCallback(userInput, dbRow, parameters, userProfile)
{
socket.emitAll("items_updated", userInput);
}
then in your angular client code, you can listen for this event by doing the following
Backand.on('items_updated', function (data) {
//Get the 'items' object that have changed
console.log(data);
});
i'm working with anonymous users, i can't use emitUsers or emitRoles to restrict to only concerned users.
As i don't want all anonymous users to receive all update events but only the ones for the item they are on (stupid to listen for updates you don't care isn't it!),
would there be a way to only listen for events on a specific item?
thanks for your help
In fact i've found the solution thanks to Backand brilliant support team (thanks Itay!).
The solution is quite simple, you can play with the event name string !
back to my example, let's say i only want the user listen for changes on the item he is actually viewing (item id is 57 for example), the code then will be:
- on the server code side
function backandCallback(userInput, dbRow, parameters, userProfile)
{
//if the updated item has an id=57 for example,
//the event emitted will be "items_updated_57"
socket.emitAll("item_updated_" + dbRow.id, userInput);
}
- on the angular client code
//currentItem id
var itemId=57;
Backand.on('item_updated_'+ itemId, function (data) {
//will be triggered when we receive an event named
// "item_updated_57"
console.log(data);
});
I understand that Ext.Msg.Confirm is asynchronous, and will proceed if you do not provide a callback.
Below is my code - this is called if the user tries to navigate from the current screen while in edit mode. I'm passing in a callback to the method - fn is my callback event, but regardless of how I code this the logic continues to navigate away from the page. Is there anyway to stop propagation until the user has selected yes or no in the confirmation box?
Here is the code for the Confirmation:
displaySaveConfirmation: function(fn) {
var title = 'Do you want to Save?'
var msg = 'You are currently Editing Standard Text. Would you like to save your changes?'
var box = Ext.Msg.confirm(title, msg, function(buttonId, value) {
if (buttonId === 'no'){
fn.call();
} else {
return false;
}
}, this );
box.setZIndex(400);
box.setCls('alert-box');
var buttons = Ext.ComponentQuery.query('button', box);
for (var i=0; i<buttons.length; i++) {
if (buttons[i].getItemId()=="no")
buttons[i].addCls('blackBtn');
else if (buttons[i].getItemId()=="yes")
buttons[i].addCls('blueBtn');
this.addReleaseEvent(box, buttons[i]);
}
},
As of now you do the following:
The event is fired
You open the MessageBox
Your function returns, and it does not return "false".
The browser navigates away before the user can click "yes" or "no".
What you want to do is the following:
The event is fired
You open the MessageBox
Your function returns false.
The browser does not navigate away.
The user clicks "yes" or "no".
If the answer is "yes", you navigate to the previously requested target page manually (e.g. close the browser window, use history.back() or similar).
Alternatively, if the work to get that up and running (and tested, with all the possible cases how to navigate away and how to find the target) is too much, you can opt for the window.alert dialog instead, which does not look nearly as nice, but works synchronously.
Or you can do as I did: I just returned false after bringing up a MessageBox that says: "You cannot leave the page during edit. Please save or abort the post."
I have an input box for a new user to put in a userid. I want to dynamically check if the userid is already taken on-blur. I'm using bootstrap validation for form validation, and I'm having a hard time figuring out how to make it do what I want. In the validation, I have this code currently for userid:
userid: {
container: '#useridMessage',
validators: {
notEmpty: {
message: '*Required'
},
stringLength: {
min:3,
message: 'Username must be at least 3 characters long'
}
}
},
What I want to be able to do in there is call a function I have for the on-blur action:
$scope.userCheck = function () {
userAPIService.getUserDetails($scope.user.username).success( function ( data ) {
if(data.Status == 0){
//make box red and invalid
}
}).error( function ( error ) {
$scope.status = "Error is checking Username availability";
})
}
My backend function is the getUserDetails which is a GET function. I was planning on, as is shown in the code, to check if the userid is a valid one (data.status == 0) already in our system and notify the user after that it is already taken. My desire is to maintain good coding practice and not by pass my bootstrap validation ( which is working well for everything else ) and make both work together, but since bootstrap validation is kind of a black box, I'm not sure how to do this.
Question: How can I get the validation to check some boolean variable, or how can I get the validation to make the backend call and then analyze the result like I want?
Thoughts:
Ideally I'd like to be able to call the onblur function which would set some boolean and then the validation would just look at the validation, but I'm open to any idea to get this right. Thanks
I wonder if creating a custom validation directive would be a good approach. There's a good overview here How to add custom validation to an angular js form, also in the Custom Validation section of the Angular JS Forms documentation. Hope this helps.