basic firebase + dialogflow - repeated agent add dialogflow - firebase

really appreciate the helps
I have been following this video with this code.
My code looks like this
function angerEmotionCapture(agent) {
const angryTo = agent.parameters.angryDirectedTo;
return admin.database().ref('directedTo').transaction((directedTo)=>{
let target = directedTo;
agent.add(`previous entry was ${target}`);
target = angryTo;
agent.add(`new entry is ${target}`);
return directedTo;
});
}
The purpose of this is to capture a conversation topic and store it in the database.
I'm planning to use it for multiple purposes that's why I don't use context.
This code is only the first step to see if I can capture it properly.
When doing this, the agent response always look like this
previous entry was null
new entry is boss
previous entry was friends
new entry is boss
Here "friends" and "boss" are expected. However, the first repetition is not expected and it always gives null. Despite of that, this correctly update the database
I want to understand why is there a repetition here
Thanks, really appreciate the time

Related

Firebase + DialogFlow - Realtime database- Cloud function return query result only after second request

I'm writing a simple Google Action which will read the Firebase Realtime Database, and return result in the response. My problem is, the query result is being passed back in response to DialogFlow only after at least 2 attempts.
Below the screenshots showing the end result in the Simulator
First query screenshot
The first line of the response is returned from the Cloud Function, and contains values passed with the "Context". There is no second line in this response.
below is the screen showing the result after sending exactly the same request second time.
Second query screenshot
First line is the same as previously, but this time I also get the second line which contains the query result data.
It looks like my code is "working" (I get the correct data from the database), but for some reason it only works if I trigger it at least 2 times in quick succession.
Below is the code snipped which handle this request:
function googleAssistantHandler(agent) {
let conv = agent.conv();
let outCommandContext = agent.getContext('outcommand');
let outCharacterContext = agent.getContext('outcharacter');
let character = outCharacterContext.parameters.character;
let command = outCommandContext.parameters.command;
agent.add('<prosody rate="140%" pitch="0.4">' + character +' '+ command +'</prosody>');
var movesRef = admin.database().ref('characters/'+character.toLowerCase()+'/moves/');
movesRef.limitToFirst(1).orderByChild("notation")
.equalTo(command.toString()).on("child_added",function(snapshot){
agent.add(`record number is ` + snapshot.key);
});
}
I've tried using once() instead of on() (as it would make more sense in my case... i don't need to listen to changes on the database, i just want to retrieve data once)- but, I couldn't get it to work.
Can you guys help me out understanding why my query returns result only after the second trigger?
Thanks!
you are using a callback method to get the data from database so there is no guaranty that it will be called before your function is returned. to solve the issue, you need to use a Promise and return that Promise in your function so the last few lines of your function will look like this
return movesRef.limitToFirst(1).orderByChild("notation")
.equalTo(command.toString()).on("child_added").then(snapshot= > {
agent.add(`record number is ` + snapshot.key);
});
You need to always use promises when working with databases. Moreover, the first response that you see might be because of the failed function which timed out. If you see your console logs in firebase, you might see the errors. Also check your default response, if it has the text that User said $name or something similar, then that is what causes the issue in the first attempt.
If you still don't get it to work, try logging the returned data and post your logs here.

Retrieve and display last entry indatabase

Frank ยท 2 hours ago
Using Ionic 2, I am unsuccessfully trying to retrieve and display the newGoalWt, newMaxReps, newMinReps, repsToday, and wtToday shown at the bottom of the firebase data image. I do not want to retrieve the other data. I thought I found the answer to this several times, but I haven't. I am not trying to get others to do work I should be doing but I am new to coding and need help at this point. If you have another course that covers this by example please let me know. sample database
I hope this makes sense/is clear.
First thing is to create a provider which gets the details. So in the provider there will be a function like this :
public getRequiredObject(objectKey : String){
return this.database.object('wideGripBPTwo-list/'+ objectKey)
}
Then in the controller/typescript file i would have a variable assigned to the return value of the provider :
this.variableName = this.Provider.getRequiredObject(objectKey);
this.variableName.subscribe(snapshot => {
this.newGoalWt = snapshot.newGoalWt;
this.newMaxReps = snapshot.newMaxReps;
//add other variables here
Lastly in the html file just bind the data :
<ion-label>{{newGoalWt}}</ion-label>
Note I have not added entire provider, .ts file and html file.
Please advise if this makes sense and if you need more assistance.

Deleting user account triggers deletion of another nodes

Db structure:
--followers
-followedUser1
-user1
-followedUser2
-user1
-user2
--users
-user1
-followed
-followedUser1
-followedUser2
-user2(followedUser1)
-followed
-followedUser2
-user3(followedUser2)
Everytime user follows(onCreate) & unfollows(onDelete) under followers/{followedUser}/{followerUser} path, it triggers function which increment or derement count and assigning or detaching posts from follower. It's working via fanout method, and there're no problems. Now, worse part comes when some user deletes account completely together with detaching his followers from himself(because his account will be a ghost), i've set trigger onDelete to indicate whenever it'll happen, then iterating through this user's (i.e.user3) followers removes himself from corresponding followers plus his account, it looks like this then:
--followers
-followedUser1
-user1
-followedUser2
-user1
-user2
--users
-user1
-followed
-followedUser1
-user2(followedUser1)
Now, problematic part - when promise returns i'd like to also remove whole follower/followedUser2(because it's a ghost now) path but... there is a trigger which unfortunately executes for every follower under(onDelete). So, is there any chance to remove path above(levelup) deletetion trigger without triggering children itself? Or any other approach would be great, thanks
edit: Dont get me wrong, it'll work but if number of followers under followedUser would be "a lot" server will die after 100... trigger
At this time, there is no way to filter what deletion events will trigger the function, so you are correct, the function will be triggered once for each user the deleted user was following. I recognize this is one of many use cases where such functionality would be useful, so if you get a chance, please fill out a feature request here.
It looks like your multiple-update problem can be fixed with a multi-location updates
In very quickly hacked together and not tested typescript:
export const cleanupFollowers = functions.auth.user().onDelete(event => {
const user = event.data.userId;
const followersNode = admin.database().ref(`followers/${user}`);
const followers = _.keys(await followersNode.once('value'));
// Every follower also has a reverse node for this user. Get the list of keys:
const reverseNodesToDelete = followers.map(follower => `followers/${follower}/${user}`);
// Model this update as a map of deep key -> null to delete all at once
let cleanup = _.keyBy(reverseNodesToDelete, null);
// add one more update: deleting full node for the deleted user.
cleanup[`followers/${user}`] = null;
// do all deletions as one database request:
return admin.database().ref().update(cleanup);
}
Note that this will still fire your counting function, but that should be fine to run in parallel. It probably makes your app simpler to have each invariant captured separately.

Firebase and Angularfire nightmare migration for Update

I am new to firebase and I am having a bit of a nightmare trying to adapt old code to what is now deprecated and what is not. I am trying to write a function which updates one "single" record in my datasource using the now approved $save()promise but it is doing some really strange stuff to my data source.
My function (should) enables you to modify a single record then update the posts json array. However, instead of doing this, it deletes the whole datasource on the firebase server and it is lucky that I am only working with testdata at this point because everything would be gone.
$scope.update = function() {
var fb = new Firebase("https://mysource.firebaseio.com/Articles/" + $scope.postToUpdate.$id);
var article = $firebaseObject(ref);
article.$save({
Title: $scope.postToUpdate.Title,
Body: $scope.postToUpdate.Body
}).then(function(ref) {
$('#editModal').modal('hide');
console.log($scope.postToUpdate);
}, function(error) {
console.log("Error:", error);
});
}
Funnily enough I then get a warning in the console "after" I click the button:
Storing data using array indices in Firebase can result in unexpected behavior. See https://www.firebase.com/docs/web/guide/understanding-data.html#section-arrays-in-firebase for more information. Also note that you probably wanted $firebaseArray and not $firebaseObject.
(No shit?) I am assuming here that $save() is not the right call, so what is the equivalent of $routeParams/$firebase $update()to do a simple binding of the modified data and my source? I have been spending hours on this and really don't know what is the right solution.
Unless there's additional code that you've left out, your article $firebaseObject should most likely use the fb variable you created just before it.
var article = $firebaseObject(fb);
Additionally, the way in which you're using $save() is incorrect. You need to modify the properties on the $firebaseObject directly and then call $save() with no arguments. See the docs for more.
article.Title = $scope.postToUpdate.Title;
article.Body = $scope.postToUpdate.Body;
article.$save().then(...

Meteor realtime game - match two players according to their score?

I want to build a realtime quiz game which randomly matches two players (according to their winning rate if they are logged in). I've read through the book Discover Meteor and have a basic understanding of the framework, but I just have no idea of how to implement the matching part. Anyone know how to do that?
if you want to match users who have scores close to each other, you can do something like this : mongodb - Find document with closest integer value
The Meteor code for those Mongo queries is very similar, but there are some subtle differences that are kind of tricky. In Meteor, it would look something like this :
SP // "selected player" = the User you want to match someone up with
var score = SP.score; // selected player's score
var queryLow = {score: {$lte:score},_id:{$ne:SP._id}};
var queryHigh = {score:{$gte:score},_id:{$ne:SP._id}};
// "L" is the player with the closest lower score
var L=Players.findOne(queryLow,{sort:{score:-1},limit:1});
// "H" is the player with the closest higher score
var H=Players.findOne(queryHigh,{sort:{score:1},limit:1});
so, now you have references to the players with scores right above and right below the 'selected player'. In terms of making it random, perhaps start with a simple algorithm like "match me with the next available player who's score is closest" , then if it's too predictable and boring you can throw some randomness into the algorithm.
you can view the above Meteor code working live here http://meteorpad.com/pad/4umMP4iY8AkB9ct2d/ClosestScore
and you can Fork it and mess about with the queries to see how it works.
good luck! Meteor is great, I really like it.
If you add the package peppelg:random-opponent-matcher to your application, you can match together opponents like this:
On the server, you need to have an instance of RandomOpponentMatcher like this:
new RandomOpponentMatcher('my-matcher', {name: 'fifo'}, function(user1, user2){
// Create the match/game they should play.
})
The function you pass to RandomOpponentMatcher will get called when two users been matched to play against each other. In it, you'll probably want to create the match the users should play against each other (this package does only match opponents together, it does not contain any functionality for playing games/matches).
On the client, you need to create an instance of RandomOpponentMatcher as well, but you only pass the name to it (the same name as you used on the server):
myMatcher = new RandomOpponentMatcher('my-matcher')
Then when the users is logged in and which to be matched with a random opponent, all you need to do is to call the add method. For example:
<template name="myTemplate">
<button class="clickMatchesWithOpponent">Match me with someone!</button>
</template>
Template.myTemplate.events({
'click .clickMatchesWithOpponent': function(event, template){
myMatcher.add()
}
})
When two different logged in users has clicked on the button, the function you passed to RandomOpponentMatcher on the server will get called.
One implementation might be as follows:
A user somehow triggers a 'looking for game' event that sets an attribute on user.profile.lookingForGame to true. The event then makes a call to a server side Meteor method which queries for all other online users looking for games.
From there you it really depends on how you want to handle users once they 'match'.
To determine all online users, try using the User Status package:
https://github.com/mizzao/meteor-user-status
Once added, any online user will have an attribute in the profile object of 'online'. You can use this to query for all online users.

Resources