Alexa Lambda KeyError: 'intent' - python

I don't know a lot about programming but have been working with raspberry Pi for a few years. I wanted to use Alexa in the Pi and run scripts to turn on and off the GPIOS. After trying some tutorials I got to the point where I connected AWS Lambda with the Alexa skill. The problem is that I get an error when testing the skill.
"Skill response was marked as failure
Luces
Request Identifier: amzn1.echo-api.request.49687858-4c4f-482f-b82d-dd0ffedc9841
The target Lambda application returned a failure response"
I check the log on the AWS cloud and this is what I got.
'intent': KeyError
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 40, in lambda_handler
intent_name = event['request']['intent']['name']
KeyError: 'intent
I don't have a clue of what to do. I adapted the code from a hackster.io tutorial. My Lambda code with python 2.7 is:
import boto3
access_key =
access_secret =
region =
queue_url =
def build_speechlet_response(title, output, reprompt_text, should_end_session):
return {
'outputSpeech': {
'type': 'PlainText',
'text': output
},
'card': {
'type': 'Simple',
'title': "SessionSpeechlet - " + title,
'content': "SessionSpeechlet - " + output
},
'reprompt': {
'outputSpeech': {
'type': 'PlainText',
'text': reprompt_text
}
},
'shouldEndSession': should_end_session
}
def build_response(session_attributes, speechlet_response):
return {
'version': '1.0',
'sessionAttributes': session_attributes,
'response': speechlet_response
}
def post_message(client, message_body, url):
response = client.send_message(QueueUrl = url, MessageBody= message_body)
def lambda_handler(event, context):
client = boto3.client('sqs', aws_access_key_id = access_key, aws_secret_access_key = access_secret, region_name = region)
intent_name = event['request']['intent']['name']
if intent_name == "LightOn":
post_message(client, 'on', queue_url)
message = "on"
elif intent_name == "LightOff":
post_message(client, 'off', queue_url)
message = "off"
else:
message = "Unknown"
speechlet = build_speechlet_response("Mirror Status", message, "", "true")
return build_response({}, speechlet)
I just want to turn the led on and then off but I don't know if I need all that code. If there is a simple way to code this in lamba please tell me.
Thank you very much in advance for the help!

There are several different request types, the "standard ones" being: CanFulfillIntentRequest, LaunchRequest, IntentRequest, and SessionEndedRequest.
You first open the skill, it'll be a LaunchRequest that's sent - which does not contain the intent parameter. That's likely why you're getting the KeyError.
A good approach is to check the request type before you try to process it.
if event['request']['type'] == "LaunchRequest":
print("I'm a launch request.")
elif event['request']['type'] == "IntentRequest":
print("I'm an intent request.")
The various request types and the parameters they can hold can be found in the Alexa documentation.

Related

How to get a proper signature to authenticate to private websocket in Wootrade

I am trying to authenticate to private websocket on wootrade
I am keep getting this error :
{
'errorMsg': 'signature error',
'event': 'auth',
'id': '123r',
'success': False,
'ts': 1644835797096
}
How should a message that I am hashing looks like and what should I correct in the code?
API = "xxx"
SECRET = "yyy"
APPLICATION_ID = "zzz"
timestamp_unix = int(time.time()*1000)
def on_message_woo(ws_woo, message):
json_message = json.loads(message)
pprint.pprint(json_message)
def on_error_woo(ws_woo, error):
print(error)
socket_woo=f'wss://wss.woo.network/v2/ws/private/stream/{APPLICATION_ID}'
ws_woo = websocket.WebSocketApp(socket_woo, on_message=on_message_woo, on_error=on_error_woo)
msg=f'|{timestamp_unix}' # I tried many combination here
signature1=hmac.new(SECRET.encode('utf8'), msg.encode('utf8'), hashlib.sha256).digest()
signature=base64.b64encode(signature1).decode('utf8')
def subskrypcje():
time.sleep(5)
ws_woo.send(json.dumps(
{
"id":"123r",
"event":"auth",
"params":{
"apikey":API,
"sign":signature,
"timestamp":timestamp_unix
}
}
))
def ws_woo_start():
ws_woo.run_forever()
t1=threading.Thread(target=ws_woo_start)
t2=threading.Thread(target=subskrypcje)
t1.start()
t2.start()
Below link to documentation
https://docs.woo.org/#auth
After consulting with a friend we found a working solution
Subscription fuction should looks as fallow:
def subskrypcje():
time.sleep(5)
timestamp_unix = str(int(time.time()*1000))
msg=f'|{timestamp_unix}'
bytes_key = bytes(str(SECRET), "utf-8")
bytes_msg = bytes(msg, "utf-8")
signature = (
hmac.new(bytes_key, msg=bytes_msg, digestmod=hashlib.sha256)
.hexdigest()
.upper()
)
ws_woo_private.send(json.dumps(
{
"id":"123r",
"event":"auth",
"params":{
"apikey":API,
"sign":signature,
"timestamp":timestamp_unix
}
}
))
The rest stays the same with this difference that hasing process is now in subscription(authantication) function

Azure function unable to send messages to topic

I am trying to solve this issue on which I don't understand fully why is not working.
I have 2 topics. TOPIC_Aon which I can send my messages and I receive them correctly. And once the message has been received, I would like to send it to another topic, TOPIC_B. So far I have been testing this code locally and everything worked just fine. But since when I started using an azure.function servicebus. The code start acting funny. And here is my code:
import logging
import azure.functions as func
import json
import boto3
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential
from azure.servicebus import ServiceBusClient, ServiceBusMessage
def main(message: func.ServiceBusMessage):
logging.info(message)
print(message)
#KeyVault Configuration
KeyVault_Url = f'url'
credential = DefaultAzureCredential()
client_keyvault = SecretClient(vault_url=KeyVault_Url, credential=credential)
# # Service Bus Connection string
CONNECTION_STR = client_keyvault.get_secret("CONN").value
# For receiving the feedback from campaigns
TOPIC_NAME_A = "TOPICA"
SUBSCRIPTION_NAME = "XXX"
# For sending feedback and results of sentiment analysis and language detection
TOPIC_NAME_B = "TOPICB"
comprehend = boto3.client(service_name='comprehend', region_name='eu-west-1', aws_access_key_id=client_keyvault.get_secret("ID").value, aws_secret_access_key=client_keyvault.get_secret("SECRET").value)
# This block will receiver the messages from the service bus listed above.
# Please mind, once the message get received and printed (json format) that event will be destroyed from the portal service bus.
servicebus_client = ServiceBusClient.from_connection_string(conn_str=CONNECTION_STR)
with servicebus_client:
receiver = servicebus_client.get_subscription_receiver(
topic_name=TOPIC_NAME_A,
subscription_name=SUBSCRIPTION_NAME
)
with receiver:
received_msgs = receiver.receive_messages(max_message_count=10, max_wait_time=60)
output_global = {}
for msg in received_msgs:
message1 = str(msg)
res = json.loads(message1)
# extracting the text from the message from service bus
text = res['Text']
#passing the text to comprehend
result_json= json.dumps(comprehend.detect_sentiment(Text=text, LanguageCode='en'), sort_keys=True, indent=4)
result = json.loads(result_json) # converting json to python dictionary
print(result)
# logging.info("Result from comprehend" , result)
#extracting the sentiment value
sentiment = result["Sentiment"]
#extracting the sentiment score
if sentiment == "POSITIVE":
value = round(result["SentimentScore"]["Positive"] * 100,2)
elif sentiment == "NEGATIVE":
value = round(result["SentimentScore"]["Negative"] * 100,2)
elif sentiment == "NEUTRAL":
value = round(result["SentimentScore"]["Neutral"] * 100,2)
elif sentiment == "MIXED":
value = round(result["SentimentScore"]["Mixed"] * 100,2)
# To detect the language of the feedback, the text received from service bus is passed to the function below
lang_result=json.dumps(comprehend.detect_dominant_language(Text = text), sort_keys=True, indent=4)
#converting languages detection results into a dictionary
lang_result_json=json.loads(lang_result)
#Formatting the score from the results
for line in lang_result_json["Languages"]:
line['Score'] = round(line['Score']* 100, 2)
#storing the output of sentiment analysis, language detection and ids in a dictionary and converting it to JSON
output = {
'XXX': res['XXX'],
'XXX Id': res['XXX'],
'XXX': res['XXX'],
'XXX': res['XXX'],
'XXX': res['XXX'],
'Sentiment': sentiment,
'Value': value,
'Languages': lang_result_json['Languages']
}
# logging.info("Message Body: " + output)
output_json = json.dumps(output, ensure_ascii=False)
#-------------------------------------------------------------------------------------------------------
# Sending the processed output (output_json) in json format to another service bus
def send_output(sender):
message2 = ServiceBusMessage(
output_json,
content_type="XXX", #setting the content type so that the service bus can route it.
ApplicationProperties={b'tenantcode':msg.ApplicationProperties[b'tenantcode']} #setting the tenant code
)
sender.send_messages(message2)
servicebus_client = servicebus_client.from_connection_string(conn_str=CONNECTION_STR, logging_enable=True)
with servicebus_client:
sender = servicebus_client.get_topic_sender(topic_name=TOPIC_NAME_B)
with sender:
send_output(sender)
this is my host.json
{
"version": "2.0",
"extensions": {
"serviceBus": {
"messageHandlerOptions": {
"autoComplete": true
}
}
},
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[2.*, 3.0.0)"
}
}
and this is my function.json
{
"scriptFile": "outthinkServiceBus.py",
"entryPoint": "main",
"bindings": [
{
"name": "message",
"type": "serviceBusTrigger",
"direction": "in",
"topicName": "XXX",
"subscriptionName": "XXX",
"connection": "XXX"
}
]
}
Inside the received I have a for loop msg on which I would like to loop over all the messages inside the topic, and one by one send them to the topicB.
Everything works fine as it is, and in the output of azure function, I can see this message
2021-10-15 15:23:45.124
Message receiver b'receiver-link-' state changed from <MessageReceiverState.Open: 3> to <MessageReceiverState.Closing: 4> on connection: b'SBReceiver-'
Information
2021-10-15 15:23:45.552
Shutting down connection b'SBReceiver-'.
So the processing gets until the receiver, but the function sender, never get triggered.
If I remove the for loop, the code works just fine. I am able to see the sender triggering and completing successfully.
Any help to understand where is the mistake and what I am doing wrong?
Thank you so much for any help you can provide me with. Andplease if you need more info just ask
UPDATE:
I have tried to indent the send_out function outside the for loop, in this case the function sent_out triggers but the azure function Servicebus fails as the output_json is out of scope. So far the only thing I could figure out is that for some reason, the function defined in the loop, never fires.

Post to Google Analytics, works from Node, fails from Python.... why?

I'm trying to post events to Google Analytics. It works fine when I do it using the NodeJS code below, but fails when I use the Python code below. Both do return a HTTP 200 and even when posting to the debug URL (https://www.google-analytics.com/debug/collect) Google Analytics returns success details in both cases (see valid: true in the response below). The problem is that when posting from NodeJS the result shows up in the GA website, when posting from Python it never shows up. I did compare the requests for both and have not been able to spot a difference.
{
"hitParsingResult": [ {
"valid": true,
"parserMessage": [ ],
"hit": "/debug/collect?v=1\u0026t=event\u0026tid=XXXXXXX\u0026cid=YYYYYYu0026ec=Slack\u0026ea=SlashCommand\u0026el=whowasat-curl\u0026an=staging.Whereis-Everybody?\u0026aid=staging.whereis-everybody.com"
} ],
"parserMessage": [ {
"messageType": "INFO",
"description": "Found 1 hit in the request."
} ]
}
The NodeJS code is (result does show up in Google Analytics):
'use strict';
var request = require('request');
require('request-debug')(request);
function postEventToGA(category, action, label) {
var options = {
v: '1',
t: 'event',
tid: process.env.GOOGLEANALYTICS_TID,
cid: process.env.GOOGLEANALYTICS_CID,
ec: category,
ea: action,
el: label,
an: process.env.STAGE_INFIX + "appname",
aid: process.env.STAGE_INFIX + "appname"
};
console.log("payload: " + JSON.stringify(options))
request.post({ url: 'https://www.google-analytics.com/collect', form: options }, function (err, response, body) {
console.log(request)
if (err) {
console.log("Failed to post event to Google Analytics, error: " + err);
} else {
if (200 != response.statusCode) {
console.log("Failed to post event to Google Analytics, response code: " + response.statusCode + " error: " + err);
}
}
});
}
postEventToGA("some-category", "some-action", "some-label")
And the Python code is (result does not show up in Google Analytics):
import json
import logging
import os
import requests
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
GOOGLEANALYTICS_TID = os.environ["GOOGLEANALYTICS_TID"]
GOOGLEANALYTICS_CID = os.environ["GOOGLEANALYTICS_CID"]
STAGE_INFIX = os.environ["STAGE_INFIX"]
def post_event(category, action, label):
payload = {
"v": "1",
"t": "event",
"tid": GOOGLEANALYTICS_TID,
"cid": GOOGLEANALYTICS_CID,
"ec": category,
"ea": action,
"el": label,
"an": STAGE_INFIX + "appname,
"aid": STAGE_INFIX + "appname",
}
response = requests.post("https://www.google-analytics.com/collect", payload)
print(response.request.method)
print(response.request.path_url)
print(response.request.url)
print(response.request.body)
print(response.request.headers)
print(response.status_code)
print(response.text)
if response.status_code != 200:
LOGGER.warning(
"Got non 200 response code (%s) while posting to GA.", response.status_code
)
post_event("some-category", "some-action", "some-label")
Any idea why the NodeJS post will show up in Google Analytics and the Python post does not?
(while both return a HTTP200)
Did some more testing and discovered that the user agent HTTP header was causing the problem. When I set it to an empty string in the Python code it works. Like this:
headers = {"User-Agent": ""}
response = requests.post(
"https://www.google-analytics.com/collect", payload, headers=headers
)
The documentation at https://developers.google.com/analytics/devguides/collection/protocol/v1/reference does state that the user agent is used, but does not clearly state what the requirements are. "python-requests/2.22.0" (default value by python-requests lib) is apparently not accepted.

Google Analytics API(Client library for Python) - Error handling

I am using Batching requests in Google Analytics API(Python). Link to Batching : https://developers.google.com/api-client-library/python/guide/batch
Batching works fine when all the records via .add() are correct(valid). When one or more values are invalid, then the batching fails for all the records.
I added a call back function to handle the error and I saw that BAtching request is failing for all the records in the batch ( as opposed to only the invalid record). Is there a way to handle the error and skip the row/record which is invalid and continue with the rest of the records in the batch?
Below is the sample code I used and the error message :
def add_user_callback(request_id, response, exception):
if exception:
print "error :",exception
else:
print "successful"
def main():
## code to set the account, property and other variables
batch.add(service.management().webpropertyUserLinks().insert(
accountId=account_id,
webPropertyId=property_at,
body={
'permissions': {
'local': [
'READ_AND_ANALYZE'
]
},
'userRef': {
'email': 'valid_address#domain.com'
}
}))
batch.add(service.management().webpropertyUserLinks().insert(
accountId=account_id,
webPropertyId=property_at,
body={
'permissions': {
'local': [
'READ_AND_ANALYZE'
]
},
'userRef': {
'email': 'invalid_address#ddomain.com' ## i used a dummy id : pppp#domain.com
}
}))
batch.execute()
#Error :
#error : <HttpError 400 when requesting https://www.googleapis.com/analytics/v3/management/accounts/62974313/webproperties/UA-62974313-35/entityUserLinks?alt=json returned "Value for field user.email = ppppp#domain.com is not valid.">
#error : <HttpError 400 when requesting https://www.googleapis.com/analytics/v3/management/accounts/62974313/webproperties/UA-62974313-11/entityUserLinks?alt=json returned "Value for field user.email = ppppp#domain.com is not valid.">
Please let me know if you need more info.
Let's assume you have a list of users you want to add to profiles stored in a list users.
You can remove the bad emails with the following callback function:
def call_back(request_id, response, exception):
if exception is not None:
if isinstance(exception, HttpError):
message = json.loads(exception.content)['error']['message']
bad = 'Value for field user.email = (\S*) is not valid.'
match = re.match(bad, message)
if match:
bad_user = match.group(1)
if bad_user in users:
users.remove(bad_user)
else:
print response
After all the failed calls return you can re-attempt the batch call again by looping through the users and constructing a new batch request:
batch = BatchHttpRequest(callback=call_back)
for user in users:
request = analytics.management().profileUserLinks().insert(
accountId=ACCOUNT_ID,
webPropertyId=PROFILE_ID,
profileId=profile,
body={
'permissions': {'local': ['READ_AND_ANALYZE']},
'userRef': {'email': user}
}
)
batch.add(request, request_id=PROFILE_ID + user)
batch.execute()

Getting Call Status with Sinch API

After placing a call with Sinch API using a 'ttsCallout' method, I'm trying to get the status for that call. The calls goes through, I'm receiving its 'callId'. But when it comes to requesting my call's status, I receive 200 OK as a HTTP Response and empty ('null') content.
First, I though something is wrong with my code and the way I form Authorization headers (I use Python, its requests library and basic auth), but then I tried Sinch API explorer (https://www.sinch.com/dashboard/#/api) and it shows the same output to me (see the attached screenshot). Have to notice that this code worked about a month ago, and I was able to get call status.
Also, here is the code I use to communicate to Sinch API.
class CallNotification(Notification):
def __init__(self, data):
self.id = ""
self.call = self.data['call']
self.api_url = self.call['api_url']
self.auth = ("application:" + self.call['auth']['key'], self.call['auth']['secret'])
self.status = {'http': "new", 'result': "none", 'reason': "none"}
def send(self):
request_url = self.api_url + "/callouts"
request = requests.post(request_url,
auth=self.auth,
json={
'method': "ttsCallout",
'ttsCallout': {
'cli': "private",
'destination': {
'type': "number",
'endpoint': self.call['to']
},
'domain': "pstn",
'custom': "customData",
'locale': "en-US",
'text': self.call['message']
}
},
headers={'content-type': 'application/json'})
self.id = json.loads(request.content)['callId']
self.status['http'] = request.reason
return self.status['http']
def getstatus(self):
if not self.status['http'] == "new":
request_url = self.api_url + "/calls/id/" + self.id
request = requests.get(request_url,
auth=self.auth,
headers={'Content-type': 'application/json'})
try:
self.status['result'] = json.loads(request.content)['result']
except TypeError:
self.status['result'] = "Sent/Unknown"
return self.status['result']
Am I doing something wrong? What do I do to get status for a placed call?

Categories