Google Action | Agent context is giving '-' in agent.context.get() - dialogflow-es

My submitted action as it is not responding properly. I found that dialog flow action is giving '-' for agent.context.get('<name>') due to which I am getting 'undefined' as final result. I have never been able to replicate this issue at my end on real device after several long retries.
Code snippet:
//agent set context
const context = {
'name': 'riddle-index',
'lifespan': 10,
'parameters': {
'rindex': index
}
};
agent.context.set(context);
//agent get context
let riddleIndex = agent.context.get('riddle-
index');
My package.json contains
"dependencies": {
"actions-on-google": "^2.6.0",
"dialogflow-fulfillment": "^0.6.1",
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.0.0"
},
//Logs agent context:
When context is having '-':
agent context {"contexts":{"-":{"name":"-","parameters":{"no-input":0,"no-match":0,"any":"Violin","any.original":"violin"}}},"session":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS","inputContexts":{"-":{"name":"-","parameters":{"no-input":0,"no-match":0,"any":"Violin","any.original":"violin"}}}}
When context is having slot values:
agent context {"contexts":{"riddle-index":{"name":"riddle-index","lifespan":10,"parameters":{"rindex":13,"any":"Violin","any.original":"violin"}},"actions_capability_audio_output":{"name":"actions_capability_audio_output","parameters":{"any":"Violin","any.original":"violin"}},"actions_capability_media_response_audio":{"name":"actions_capability_media_response_audio","parameters":{"any":"Violin","any.original":"violin"}},"actions_capability_account_linking":{"name":"actions_capability_account_linking","parameters":{"any":"Violin","any.original":"violin"}},"google_assistant_input_type_voice":{"name":"google_assistant_input_type_voice","parameters":{"any":"Violin","any.original":"violin"}},"system_counters":{"name":"system_counters","parameters":{"no-input":0,"no-match":0,"any":"Violin","any.original":"violin"}}},"session":"projects/musical-instruments-quiz-8a073/agent/sessions/ABwppHGaPKiEmY8CePzJNdQXwQqMfKAmP0QUNIDyNfyGmuS5ScgqXa4pJKYq4B7Z52uZBXFZEIjg5YmzOWQroYudMNryOrkAmL--sEFz","inputContexts":{"riddle-index":{"name":"riddle-index","lifespan":10,"parameters":{"rindex":13,"any":"Violin","any.original":"violin"}},"actions_capability_audio_output":{"name":"actions_capability_audio_output","parameters":{"any":"Violin","any.original":"violin"}},"actions_capability_media_response_audio":{"name":"actions_capability_media_response_audio","parameters":{"any":"Violin","any.original":"violin"}},"actions_capability_account_linking":{"name":"actions_capability_account_linking","parameters":{"any":"Violin","any.original":"violin"}},"google_assistant_input_type_voice":{"name":"google_assistant_input_type_voice","parameters":{"any":"Violin","any.original":"violin"}},"system_counters":{"name":"system_counters","parameters":{"no-input":0,"no-match":0,"any":"Violin","any.original":"violin"}}}}
//Logs Request body:
Dialogflow Request body: {"responseId":"f9b56859-cb12-431a-a46c-02c92c5a64be-426bc00a","queryResult":{"queryText":"violin","parameters":{"any":"Violin"},"allRequiredParamsPresent":true,"outputContexts":[{"name":"projects/musical-instruments-quiz-8a073/agent/environments/aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/riddle-index","lifespanCount":10,"parameters":{"rindex":6,"any":"Violin","any.original":"violin"}},{"name":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/actions_capability_audio_output","parameters":{"any":"Violin","any.original":"violin"}},{"name":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/actions_capability_media_response_audio","parameters":{"any":"Violin","any.original":"violin"}},{"name":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/actions_capability_account_linking","parameters":{"any":"Violin","any.original":"violin"}},{"name":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/actions_capability_screen_output","parameters":{"any":"Violin","any.original":"violin"}},{"name":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/actions_capability_web_browser","parameters":{"any":"Violin","any.original":"violin"}},{"name":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/google_assistant_input_type_voice","parameters":{"any":"Violin","any.original":"violin"}},{"name":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS/contexts/__system_counters","parameters":{"no-input":0,"no-match":0,"any":"Violin","any.original":"violin"}}],"intent":{"name":"projects/musical-instruments-quiz-8a073/agent/intents/e74ab319-eb4e-45d5-b3cd-40b1a567fa68","displayName":"RiddleAnswer Intent"},"intentDetectionConfidence":1,"languageCode":"en"},"originalDetectIntentRequest":{"source":"google","version":"2","payload":{"user":{"locale":"en-US","userVerificationStatus":"VERIFIED"},"conversation":{"conversationId":"ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS","type":"ACTIVE","conversationToken":"[\"riddle-index\"]"},"inputs":[{"intent":"actions.intent.TEXT","rawInputs":[{"inputType":"VOICE","query":"violin"}],"arguments":[{"name":"text","rawText":"violin","textValue":"violin"}]}],"surface":{"capabilities":[{"name":"actions.capability.AUDIO_OUTPUT"},{"name":"actions.capability.MEDIA_RESPONSE_AUDIO"},{"name":"actions.capability.ACCOUNT_LINKING"},{"name":"actions.capability.SCREEN_OUTPUT"},{"name":"actions.capability.WEB_BROWSER"}]},"isInSandbox":true,"availableSurfaces":[{"capabilities":[{"name":"actions.capability.AUDIO_OUTPUT"},{"name":"actions.capability.SCREEN_OUTPUT"},{"name":"actions.capability.WEB_BROWSER"}]}]}},"session":"projects/musical-instruments-quiz-8a073/agent/environments/__aog-4/users/-/sessions/ABwppHFMeT9XwzM6qV8uaK1EiCdMMKX6WiL6CcAgXKiqAjRg-X1au6qNz7QnHaJLSUFU_jRv4RCi5Awe2AGklCccN9nlkH8KG_4lv4fS"}

You're calling the agent.context.set() method incorrectly. This new method takes different parameters.
In the past, agent.setContext() takes an object. It seems like you're using agent.context.set like the setContext() method.
The correct signature of agent.context.set() is passing in the following parameters in order: contextName, lifespan, context parameter object);
For example,
agent.context.set('context_name_1', 10, {'param1' : 'abc'});
agent.context.set('context_name_2', 11, {'param2' : 2});

Related

Unable to add event listener to webNavigation.onCompleted

Using the mock function below along with the dev console:
This call will work:
chrome.webNavigation.onCompleted.addListener(processWebNavChange, filtera);
but when I actually pass in my real var filter it throws this error:
Uncaught TypeError: Could not add listener
My actual data looks like this:
{
url: [ {hostContains: ".im88rmbOwZ"} ]
}
function registerWebNavListener() {
var matchers = getUrlMatchers();
var filter = {
url: matchers
};
// test with mock data filtera that actually works
const filtera = {
url:
[
{hostContains: "example.com"},
]
}
if (matchers.length > 0) {
chrome.webNavigation.onCompleted.addListener(processWebNavChange, filtera);
}
}
async function processWebNavChange(data) {
}
Is there something wrong with my data structure that I'm actually using? I don't believe that the filter object I returned is incorrect
}
EDIT:
I added a new
const filterb = {
url: [ {hostContains: ".im88rmbOwZ"} ]
};
and it still fails with that. The single entry {hostContains: ".im88rmbOwZ"}, was the first item returned from getURLMatchers() which I used as an example of real data being returned.
The above comment on the upper-case letters was the cause of the issue. Converting everything to lowercase resolved the problem.
Although, I am not clear as to why that was a problem to begin with. (If there are any hints in the chromium source code event filter handlers, I'd appreciate it if it could be pointed out).

Swift Combine - delaying a publisher

TL;DR
I want to delay a publication, but can't figure out how to, er, combine the parts
In Brief
I have a Publisher
let generator = PassthroughSubject<Bool, Never>()
and want somehow to use the modifier
.delay(for: 2, scheduler: RunLoop.main)
so that when I call
generator.send(true)
the message is sent two seconds after the call to send()
Looking at the docs for Publishers.Delay made the type error more clear, but doesn't help me to find the right way to hook things up.
Code
import SwiftUI
import Combine
// Exists just to subscribe.
struct ContainedView : View {
private let publisher: AnyPublisher<Bool, Never>
init(_ publisher: AnyPublisher<Bool, Never> = Just(false).dropFirst().eraseToAnyPublisher()) {
self.publisher = publisher
}
var body: some View {
Rectangle().onReceive(publisher) { _ in print("Got it") }
}
}
struct ContentView: View {
let generator = PassthroughSubject<Bool, Never>()
// .delay(for: 2, scheduler: RunLoop.main)
// Putting it here doesn't work either.
var body: some View {
VStack {
Button("Tap") {
// Does not compile
self.generator.delay(for: 2, scheduler: RunLoop.main).send(true)
// Value of type 'Publishers.Delay<PassthroughSubject<Bool, Never>, RunLoop>' has no member 'send'
// https://developer.apple.com/documentation/combine/publishers/delay
// Does not compile
self.generator.send(true).delay(for: 2, scheduler: RunLoop.main)
// Value of tuple type '()' has no member 'delay'
// Just a broken-up version of the first try.
let delayed = self.generator.delay(for: 2, scheduler: RunLoop.main)
delayed.send(true)
// This, of course, builds and works.
self.generator.send(true)
print("Sent it")
}
ContainedView(generator.eraseToAnyPublisher())
.frame(width: 300, height: 200)
}
}
}
You can use the debounce property of a publisher to delay the publishing.
$yourProperty
.debounce(for: 0.8, scheduler: RunLoop.main)
.eraseToAnyPublisher()
.delay(for: 2, scheduler: RunLoop.main) is likely exactly what you need, but it'll be key to see how you're subscribing to fully understand the issue. Delay doesn't delay the sending of the value when using send() with a subject - that's a link for imperative code and sends the data whenever send is invoked, typically against some already existing subscription.
While you have a subscriber in the first bit of code, there isn't one with the subject to pin these together.
For example, if you updated:
Just(false).dropFirst().eraseToAnyPublisher()
to
Just(false).dropFirst().eraseToAnyPublisher().delay(for: 2, scheduler: RunLoop.main)
Then the print statement should trigger ~2 second after the the init() was invoked. Depending on what you're trying to accomplish here, using a closure trigger such as onAppear might make a lot more sense, having that call the subject.send(), which you can then delay as you like in the publisher chain that happens before whatever subscribes to it.
var cancellables: [AnyCancellable] = []
let generator = PassthroughSubject<Bool, Never>()
let generated = generator.delay(for: 2, scheduler: RunLoop.main).sink { value in
print(value.description + " " + Date().timeIntervalSinceReferenceDate.description)
}
print(Date().timeIntervalSinceReferenceDate.description)
generator.send(true)
generator.send(false)
output
641453284.840604
true 641453286.841731
false 641453286.847715

Dialogflow Context

I got some problem, I can't access my parameters from context on dialogflow, i just trying using agent.getContext and agent.context.get but still not work.
there is my code for set the context
function noTelp(agent){
const telp = agent.parameters.phoneNumber;
let query = db.collection('pelanggan').where('no_telp','==',telp);
return query.get().then(snapshot => {
if (snapshot.empty) {
agent.add('Mohon Maaf data no telepon '+telp+' tidak ditemukan');
agent.add('untuk menambahkan data kamu silahkan tuliskan nama kamu');
agent.setContext({ >set the context
name : 'tambahData',
lifespan : 2,
parameters : {noTelp : telp}
});
console.log('No matching documents.');
return;
}
}
and this for the calling the context
function tambahData(agent){
const context = agent.getContext('tambahData'); >get the context
const telp = context.parameters.noTelp; >get the parameters from context
const nama = agent.parameters.nama;
agent.add(nama+telp); >test calling parameters
}
Used a consistent method either from V1 or V2. You can modify the code as below, it will work. I managed to work like this only.
Setting context:
agent.context.set({
name: 'global_main_context',
lifespan: 5,
parameters: param
});
Getting Context
let globalContext = agent.context.get('global_main_context');
I would suggest to keep updating the context in each of transaction because it as lifespan that will automatically kill that context if you cross a number of transactions.

Using find{ } on a map where the whole map is evaluated not each element

I created some mixin methods. Code and example below:
URL.metaClass.withCreds = { u, p ->
delegate.openConnection().tap {
setRequestProperty('Authorization', "Basic ${(u + ':' + p).bytes.encodeBase64()}")
}
}
URLConnection.metaClass.fetchJson = {
delegate.setRequestProperty('Accept', 'application/json')
delegate.connect()
def code = delegate.responseCode
def result = new JsonSlurper().parse(code >= 400 ? delegate.errorStream : delegate.inputStream as InputStream)
[
ok : code in (200..299),
body: result,
code: code
]
}
example usage:
new URL("$baseUrl/projects/$name").withCreds(u, p).fetchJson().find {
it.ok
}?.tap{
it.repos = getRepos(it.key).collectEntries { [(it.slug): it] }
}
}
When I dont use find(), my object is, as expected, a map with those 3 elements. When I use find it is a Map.Entry with key ok and value true
which produces this error:
groovy.lang.MissingPropertyException: No such property: ok for class: java.util.LinkedHashMap$Entry
Possible solutions: key
It occured to me when I wrote this post that it was treated the map as an iterable and thus looking at every entry which I have subsequently verified. How do I find on the whole map? I want it.ok because if it's true, I need to carry it forward
There is no such method in Groovy SDK. Map.find() runs over an entry set of the map you call method on. Based on expectation you have defined I'm guessing you are looking for a function that tests map with a given predicate and returns the map if it matches the predicate. You may add a function that does to through Map.metaClass (since you already add methods to URL and URLConnection classes). Consider following example:
Map.metaClass.continueIf = { Closure<Boolean> predicate ->
predicate(delegate) ? delegate : null
}
def map = [
ok : true,
body: '{"message": "ok"}',
code: 200
]
map.continueIf { it.ok }?.tap {
it.repos = "something"
}
println map
In this example we introduced a new method Map.continueIf(predicate) that tests if map matches given predicate and returns a null otherwise. Running above example produces following output:
[ok:true, body:{"message": "ok"}, code:200, repos:something]
If predicate is not met, map does not get modified.
Alternatively, for more strict design, you could make fetchJson() method returning an object with corresponding onSuccess() and onError() methods so you can express more clearly that you add repos when you get a successful response and optionally you create an error response otherwise.
I hope it helps.

CDON API RESTful Api GET request

I'm currently working on fetching customer data from cdon, it's an e-commerce platform. They have their API documentation here:
CDON Api Docu
First let me show you my code:
myToken = '<token here>'
myUrl = 'https://admin.marketplace.cdon.com/api/reports/d8578ef8-723d-46cb-bb08-af8c9b5cca4c'
head = {'Authorization': 'token {}'.format(myToken),
'Status':'Online',
'format':'json'}
filters = '?filter={"Status":["Online"],"format": ["json"] }}'
response = requests.get(myUrl + filters, headers=head)
report = response.json()
print(report.products)
This is returning only the parameters. like for example at at this JSON: CDON Github
Status has a value Online this online is a group of itemsthat I only want to get.
What I'm trying to get is a response like this:
{
"Products": [
{
"SKU": "322352",
"Title": "Fabric Cover",
"GTIN": "532523626",
"ManufacturerArticleNumber": "",
"StatusCDON": "Online",
"ExposeStatusCDON": "Buyable",
"InStock": 0,
"InStockCDON": 0,
"CurrentPriceSE": null,
"OrdinaryPriceSE": null,
"CurrentPriceCDONSE": 299.0000,
"OrdinaryPriceCDONSE": null,
"CurrentPriceDK": null,
"OrdinaryPriceDK": null,
"CurrentPriceCDONDK": null,
"OrdinaryPriceCDONDK": null,
"CurrentPriceNO": null,
"OrdinaryPriceNO": null,
"CurrentPriceCDONNO": null,
"OrdinaryPriceCDONNO": null,
"CurrentPriceFI": null,
"OrdinaryPriceFI": null,
"CurrentPriceCDONFI": null,
"OrdinaryPriceCDONFI": null
},
Which means the full list of the items that are Online
How should I put this... among all the API's I tried this one is very confusing, is this even RestFul? If I can achieve the python equivalent of this C# sample code:
public string Post(Guid repordId, string path)
{
var filter = new JavaScriptSerializer().Serialize(new
{
States = new[] { "0" } // Pending state
});
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair("ReportId", repordId.ToString()),
new KeyValuePair("format", "json"),
new KeyValuePair("filter", filter)
});
var httpClient = new HttpClient() { BaseAddress = new Uri("https://admin.marketplace.cdon.com/") };
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("api", ApiKey);
var response = httpClient.PostAsync(path, content).Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
I may be able to undestand how this API works, the response that I got was taken manually from their report function in JSON format.
Image
I made many attempts and at that code ( my code ) I stopped, being on this for 4 hours made me give up and ask. Trust that I have searched as many references as I could. It's really confusing.
How do I get the response that I want? Filtering via url? or via header? is this even restful? Help T_T
The documentation states in the first line, emphasis mine:
In order to generate a report you perform a POST call to the reports API with the parameters you wish to use for the report.
Your Python code does not make a POST request, you are trying a GET request. The documentation goes on
[...] to filter on Swedish orders you set the CountryCodes
attribute to “Sweden” and to get returned and cancelled orders you set
the States attribute to 2 and 3. So in the end the filter would look
like this:
{
"CountryCodes": [ "Sweden" ],
"States": ["2", "3"]
}
So you need to prepare a filter object (a dictionary in Python) with the filters you want. Luckily the Python syntax for dictionaries is equivalent (Python is flexible and also allows single-quoted strings):
filter = {
'CountryCodes': [ 'Sweden' ],
'States': [ '0' ]
}
The documentation goes on
You then post the parameters as form data (content-type:
application/x-www-form-urlencoded) so the request body would look like
this:
ReportId=d4ea173d-bfbc-48f5-b121-60f1a5d35a34&format=json&filter={"CountryCodes":["Sweden"],"States":["2","3"]}
application/x-www-form-urlencoded is the default for HTTP post, the requests module knows that and does this for you automatically. All you need to do is to prepare a data dict which will contain the data you want to post.
data = {
'ReportId': 'd4ea173d-bfbc-48f5-b121-60f1a5d35a34',
'format': 'json'
'filter': json.dumps(filter)
}
The filter parameter is supposed to be in JSON format. You must encode that yourself via json.dumps().
import json
head = { ... as above }
filter = { ... as above }
data = { ... as above }
response = requests.post(url, data, header=head)
I'll leave figuring out setting the Authorization header properly as an exercise for you. Partly because it isn't hard, partly because I have no intention of creating an API key with this website just for testing this and partly because it's entirely possible that your current header already works.

Resources