Add user to actioncable channel without page refresh. - ruby-on-rails

When I'm referring to actioncable channels, i am referring to this:
.
After creating the chatroom + chatroom_users, the user has to refresh the page to connect to that specific chatroom_id channel. Is it possible to connect to that channel without a reloading the page?

It is possible to create connection upon a custom action in your view (instead of page refresh). Please have a look at the code below,
createconusmer = (send_params) ->
App.chatbot = App.cable.subscriptions.create { channel: "ChatbotChannel" , auth_token: send_params , url: string },
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
console.log(data)
speak: (data, responder, payload) ->
#perform 'speak' , message: data , responder: responder , payload: payload
Now you can define custom function in your coffee file as,
nameclick = (value) ->
createconusmer value
window["nameclick"] = nameclick
Now in your view, you can use function nameclick to create a new streaming. Also, I am adding my bit of code to make sure they are unique or not, so to avoid addition of repititive connections.
connections = []
addConnection = (id) ->
connections.push(id)
removeConnection = (id) ->
index = connections.indexOf(id)
connections.splice(index, 1) if index > -1
connectedTo = (id) ->
connections.indexOf(id) > -1
nameclick = (value) ->
if connectedTo(value) == false
addConnection(value)
createconusmer value

Related

Send the parsed intent and slots back to the client from Amazon-Lex

Amazon Lex FAQ's mention that we can send the parsed intent and slots back to the client, so that we can place the business logic in the client. But am unable to find anything clear on this in the Lex documentation.
My use case:
Send text/voice data to Amazon lex, lex then parses the intent and slots and sends back the JSON with intent, slot and context data back the client which requested it, rather than sending it to Lambda or other backend API endpoint.
Can anyone please point out the right way/configuration for this?
Regards
If I'm understanding you correctly, you want your client to receive the LexResponse and handle it within the client rather than by Lambda or backend API. If this is correct, you can try the following implementation of Lex-Audio.
// This will handle the event when the mic button is clicked on your UI.
scope.audioClick = function () {
// Cognito Credentials for Lex Runtime Service
AWS.config.credentials = new AWS.CognitoIdentityCredentials(
{ IdentityPoolId: Settings.AWSIdentityPool },
{ region: Settings.AWSRegion }
);
AWS.config.region = Settings.AWSRegion;
config = {
lexConfig: { botName: Settings.BotName }
};
conversation = new LexAudio.conversation(config, function (state) {
scope.$apply(function () {
if (state === "Passive") {
scope.placeholder = Settings.PlaceholderWithMic;
}
else {
scope.placeholder = state + "...";
}
});
}, chatbotSuccess
, function (error) {
audTextContent = error;
}, function (timeDomain, bufferLength) {
});
conversation.advanceConversation();
};
The success function which is called after Lex has responded is as follows:
chatbotSuccess = function (data) {
var intent = data.intent;
var slots = data.slots;
// Do what you need with this data
};
Hopefully that gives you some idea of what you need to do. If you need the reference for Lex-Audio, there's a great post about it on the Amazon Blog which you should go check out:
https://aws.amazon.com/blogs/machine-learning/capturing-voice-input-in-a-browser/

Embeded Watson Virtual Agent chatbot missing response

I've created an html file with embedded Watson Virtual Agent chat bot, code similar below, with WVA strictly using the building core capabilities:
IBMChat.init({
el: 'ibm_chat_root',
baseURL: 'https://api.ibm.com/virtualagent/run/api/v1',
botID: '',
XIBMClientID: '',
XIBMClientSecret: ''
});
What I noticed is if I run the WVA in Preview mode, and have input "pay bill", the WVA can come back with two piece response, with first:
Accessing your account information...
and second the make payment:
Your account balance is $42.01 due on 5/17/2017. What would you like to do? (More options coming soon!)
However, if I enter the same in my HTML chatbot, the response only comes back with the first part:
Accessing your account information...
and second part never comes out.
Does anyone else experience the same problem?
The version in the "Preview" mode has some mock "action" handlers setup. Obviously, not every one of you users would owe $42! In the sample code on the github, the mock action handlers are not setup. There are examples on how to subscribe to those action events with handlers here: https://github.com/watson-virtual-agents/chat-widget/tree/master/examples/basic-actions-example
As of 5/31/17 you can cover all the built in actions using the code snippet below...
const config = { instance: null };
const getUserProfileVariablesMap = {
'bill_amount': '42.01',
'payment_due_date': (() => {
const currentDate = new Date(new Date().getTime() + 24 * 60 * 60 * 1000);
return `${currentDate.getMonth() + 1}/${currentDate.getDate()}/${currentDate.getFullYear()}`;
})(),
'authorized_users': 'Bob Everyman and Jane Doe'
};
const getUserProfileVariables = (data) => {
const variables = data.message.action.args.variables;
variables.forEach(v => {
const value = getUserProfileVariablesMap[v];
(value) ? config.instance.profile.set(v, value) : config.instance.profile.set(v, '[sample data]');
});
config.instance.sendSilently('success');
};
const success = () => config.instance.sendSilently('success');
const agent = () => config.instance.receive('On your own site you would run code to connect to an agent now.');
const accountSettings = () => config.instance.receive('On your own site you would run code to open the Account Settings page now.');
function registerActions(instance) {
config.instance = instance;
instance.subscribe('action:getUserProfileVariables', getUserProfileVariables);
instance.subscribe('action:updateAddress', success);
instance.subscribe('action:updateUserName', success);
instance.subscribe('action:updatePhoneNumber', success);
instance.subscribe('action:updateEmail', success);
instance.subscribe('action:payBill', success);
instance.subscribe('action:sendPaymentReceipt', success);
instance.subscribe('action:agent', agent);
instance.subscribe('action:openAccountSettingsPage', accountSettings);
};
window.IBMChatActions = {
registerActions: registerActions
};
// window.IBMChatActions.registerActions(window.IBMChat);
On the Administrative Preview, you are getting fake code stubs that handle action requests from the agent.
When one of these actions are invoked, the widget will print the "Processing..." message and then invoke all registered subscribers for that action. It is up to these registered subscribers to continue the conversation flow by silently sending "success", "failure", or "cancel" back to the server.
For example, the agent might pass down the "payBill" action. You would want to call your payment gateway, determine if it was successful, and then notify the agent of the result:
IBMChat.init(/* Settings */);
IBMChat.subscribe('action:payBill', function() {
var data = {
amount: IBMChat.profile.get('amount'),
card: {
number: IBMChat.profile.get('cc_number'),
// ... other private card data
}
};
$.post('https://www.myserver.com/payment-gateway', data)
.done( function() {
IBMChat.sendSilently('success');
})
.fail( function() {
IBMChat.sendSilently('failure');
});
});
Actions Documentation
https://github.com/watson-virtual-agents/chat-widget/blob/master/docs/DOCS.md#actions

AngularJS: Retrieve newly created Record Id in CoffeeScript

To create a new record added from a form in AngularJS, I have:
task = Task.save($scope.newTask)
But I need to also retrieve the new id that was created upon save. The answer found here: Not getting updated values after call to AngularJS $save seems to address what I need, but when I convert to CoffeeScript:
task = $scope.newTask
task.$save (msg, headers) ->
console.log task.id
It does not work. I am using Ruby on Rails. Any help is appreciated.
Edit:
I am getting the error on the code above: "Object # has no method 'save'"
Here is a bigger picture of my code:
app = angular.module("appName", ["ngResource"])
app.factory "Task", ["$resource", ($resource) ->
$resource("/projects/:project_id/tasks/:id", {project_id: "#project_id", id: "#id"}, {update: {method: "PUT"}, destroy: { method: 'DELETE' }})
]
#TaskCtrl = ["$scope", "Task", ($scope, Task) ->
$scope.tasks = Task.query()
$scope.saveTask = ->
task = $scope.newTask
task.$save (msg, headers) ->
console.log task.id
$scope.tasks.push(task)
$scope.newTask = {}
]
You are trying to save newTask as it was an instance of the Task resource, but it is not, you are creating it as an empty object ($scope.newTask = {}).
You should create it as $scope.newTask = new Task()
Or save is as you seem to suggest in the first line of code: Task.save($scope.newTask).
The result of the Task.save() call will depend on what the server returns, it's JSON data, if you return the new ID in the JSON response of the POST request, then you'll get it in your data, if you just return an empty "created" response, with a Location header, you shuld subsequently retrieve the data. If you return nothing at all, that's the point in which you touch ruby, and return something useful from the server.

Changing state of to do list tasks with Backbone in Rails app

I'm very new to Backbone and I'm making a to do list app to learn the basics. Right now you can add a task and it is rendered in the to do list. Each task has the properties name (string) and complete(boolean). I'd like to make it so that when the checkbox (.toggle) is checked, the 'complete' property is changed to true. I also have an 'x' button (.destroy), that when clicked should remove the task from the database. I'm having trouble getting the markComplete and clear events to work correctly. Here's my tasks_index.js.coffee view:
class Backbonetodo.Views.TasksIndex extends Backbone.View
template: JST['tasks/index']
events:
'submit #new_task': 'createTask'
'click .toggle': 'markComplete'
'click .destroy': 'clear'
#I'd like this to change complete to true and put a line through the list item
markComplete: ->
#collection.set(complete:true)
initialize: ->
#collection.on('reset', #render, this)
#collection.on('add', #appendTask, this)
render: ->
$(#el).html(#template())
#collection.each(#appendTask)
this
appendTask: (task) ->
view = new Backbonetodo.Views.Task(model: task)
$('#tasks').append(view.render().el)
createTask: (task) ->
event.preventDefault()
attributes = name: $('#new_task_name').val()
#collection.create attributes,
wait: true
success: -> $('#new_task')[0].reset()
error: #handleError
handleError: (task, response) ->
if response.status == 422
errors = $.parseJSON(response.responseText).errors
for attribute, messages of errors
alert "#{attribute} #{message}" for message in messages
#This should remove the selected task from the database
clear: (task) ->
event.preventDefault()
#collection.remove()
This might also help. Here's my task.js.coffee view:
class Backbonetodo.Views.Task extends Backbone.View
template: JST['tasks/task']
tagName: 'li'
render: ->
$(#el).html(#template(task: #model))
this
markComplete: (event) ->
# You need to get somehow specific task id
# Example: id = $(event.currentTarget).data('id'), if you have the id in the DOM
#collection.get(id).set(complete:true)
# You have here a parameter task which is actually event
clear: (task) ->
event.preventDefault() # This is task :p
# Same as before: get your task or task id
# Example:
# taskId = $(event.currentTarget).data('id')
# task = #collection.get(taskId)
#collection.remove(task)
My examples depends on your template.
Suggestion: try to use #$ instead of $, to scope elements to your view's element, by the way I used $

How to manage several users in nitrogen/inets erlang

I am building a simple web site for my family usage, using Nitrogen/Inets.
The current version works as expected as long as I have a single client connected to the web server.
If I try to connect a new user, the 2 sessions seems to share the same state variables:
browser 1: read the index page, click on connect, and login as user1.
browser 2: read the index page, the user is user1 --> not OK
- go to login and enter as user2. Seems ok.
browser 1: reload the home page --> user changed to user2 --> very bad :o(
in addition I have included a display to show the timeout counter -> the values are synchronized on both browser.
Can you tell me what I must do to manage several users at the same time? the code i wrote is the following:
extract of index.erl
header() ->
% if the user is not defined, show in the header a button to connect, change the title and display a reduced menu (thanks to common module)
% if the user is defined, show in the header a button to disconnect, change the title and display a complete menu (thanks to common module)
User = wf:user(),
Msg = case User of
undefined ->
"Pas connecté";
User ->
"Welcome " ++ User
end,
case User of
undefined ->
common:header(Msg, #button { id=dcnx, text="Connexion", postback=connexion , class = rightalign});
User ->
common:header(Msg, #button { id=dcnx, text="Deconnexion", postback=deconnexion , class = rightalign})
end.
body() ->
#container_12 { body= #grid_8 { alpha=true, prefix=2, suffix=2, omega=true, body=inner_body(wf:user()) }}.
inner_body(User) ->
Msg = case User of
undefined -> "Pas connecté";
_ -> User ++ " home"
end,
[
#h2{ text= Msg },
#p{}
].
event(deconnexion) ->
% a temporary popup just to display the user name and check the function call
wf:wire(#alert { text="Bye bye " ++ wf:user() }),
wf:clear_session(), % erase the session information
wf:redirect("index"); % redisplay the index page
event(connexion) ->
wf:redirect_to_login("login");
event(E) -> common:event(E).
extract of login.erl:
body() ->
% the login requires a name and a password,
% checks that {name,password} exists
Body = [
#label { text="Name" },
#textbox { id=nameTextBox, next=passwordTextBox },
#p{},
#label { text="Password" },
#password { id=passwordTextBox, next=continueButton },
#p{},
#button { id=continueButton, text="Continue", postback=continue }
],
wf:wire(continueButton, nameTextBox, #validate { validators=[
#is_required { text="Obligatoire." },
#custom { text="utilisateur invalide.", tag=tag_name, function=fun name_validator/2 }
]}),
wf:wire(continueButton, passwordTextBox, #validate { validators=[
#is_required { text="Obligatoire." },
#custom { text="mot de passe incorrect.", tag=tag_password, function=fun password_validator/2 }
]}),
Body.
event(continue) ->
Name = wf:q(nameTextBox), % retreive the name
wf:user(Name), % define as user
wf:role(admin,is_admin(Name)), % define role
wf:session(logged,true), % define as logged
wf:redirect_from_login("index"); % go back to the calling page or index if not defined
event(_) -> ok.
The session information is stored with a cookie as the key.
Are you opening the page in another window of the same browser, or are you opening a completely separate browser (IE, Chrome and Firefox)? If you're just opening another window or tab within the same browser, the cookie information is certainly going to be shared, leading to the effect you've described.
So make sure you're firing up two completely different browsers.
The code you've posted looks right - nothing stands out as sharing information across sessions.

Resources