Slack send attachment returning 500 error - python

I am trying to send an attachment to a Slack web hook.
I have followed the API documentation and can send a simple message. When I try send an attachment I get a 500 error, I presume there is an issue with my payload but I cannot work it out for the life of me.
How do I get the attachment to post successfully?
import slackweb
slack = slackweb.Slack(url='WEB HOOK URL HERE')
slack.notify(text="Maguro is a sushi")
attachments = []
attachment = {
"attachments": [
{
"fallback": "Required plain-text summary of the attachment.",
"color": "#36a64f",
"pretext": "Optional text that appears above the attachment block",
"author_name": "Bobby Tables",
"author_link": "http://flickr.com/bobby/",
"author_icon": "http://flickr.com/icons/bobby.jpg",
"title": "Slack API Documentation",
"title_link": "https://api.slack.com/",
"text": "Optional text that appears within the attachment",
"fields": [
{
"title": "Priority",
"value": "High",
"short": False
}
],
"image_url": "http://my-website.com/path/to/image.jpg",
"thumb_url": "http://example.com/path/to/thumb.png",
"footer": "Slack API",
"footer_icon": "https://platform.slack-edge.com/img/default_application_icon.png",
"ts": 123456789
}
]
}
attachments.append(attachment)
slack.notify(attachments=attachments)

You're missing two required fields in your Slack message with attachments: text and channel. You also need to lowercase the value of the short field to false.
See the corrected message here in Slack's message tester:
{
"text": "You need this field",
"channel": "C########",
"attachments": [
{
"fallback": "Required plain-text summary of the attachment.",
"color": "#36a64f",
"pretext": "Optional text that appears above the attachment block",
"author_name": "Bobby Tables",
"author_link": "http://flickr.com/bobby/",
"author_icon": "http://flickr.com/icons/bobby.jpg",
"title": "Slack API Documentation",
"title_link": "https://api.slack.com/",
"text": "Optional text that appears within the attachment",
"fields": [
{
"title": "Priority",
"value": "High",
"short": false
}
],
"image_url": "http://my-website.com/path/to/image.jpg",
"thumb_url": "http://example.com/path/to/thumb.png",
"footer": "Slack API",
"footer_icon": "https://platform.slack-edge.com/img/default_application_icon.png",
"ts": 123456789
}
]
}
Create a JSON structure including those two fields and your attachments variable, and you should be good to go.

Related

buttons from webhook dialogflow cx

I have a diaglogflow cx assistant and I have my own web front. I am getting the responses from a webhook and there is no problem with texts but I would like to add buttons, cards, images... that, although they cannot be seen in dialogflow cx, I can use the info in my front.
The problem is that dialogflow cx does not resend it in the response to the front that call it. I'm sending this response from the webhook server:
{
"fulfillment_response": {
"messages": [
{
"text": {
"text": [
"webhooktext"
]}}],
"richContent": [[
{
"type": "buttons",
"options": [
{
"text": "button 1"
},
{
"text": "button 2"
}]}]]}}
And I am receiving this in the front app:
"responseId": "fc385ea7-6f8c-4828-9e25-5196916c4028",
"queryResult": {
"text": "hey",
"languageCode": "es",
"responseMessages": [
{
"text": {
"text": [
"dialogflow text"
]
}
},
{
"payload": {
"kbID": "greeting"
}
},
{
"text": {
"text": [
"webhooktext"
]
}
}
],
"webhookPayloads": [
{}
],
"currentPage": {...},
"intent": {...},
"intentDetectionConfidence": 1.0,
"diagnosticInfo": {...},
"webhookStatuses": [...],
"match": {...}
},
"responseType": "FINAL"
}
I was expecting to receive the button data in the responseMessages array or even in webhookPayloads. I have tried to manage the webhook response structure but sometimes it gives an error message in the front dictionary and other times it shows as above.
Should I change richContext key? Where do I place it?
Text along with images and other components can be parsed in Dialogflow CX. You can refer to this documentation for response texts and parse them on your side as per your requirement. For adding visual elements , you can refer to this link.
The most common way to achieve this in Python is to use pf.json_format.MessageToJson method according to this StackOverflow case.

Access to intent slots from lambda function

I created a simple bot with a simple lex intent. This lex intent has one single slot slotTwo.
Then I linked it with a python lambda function. I want to access to the value of that slot from inside the lambda function.
python lambda function receives the parameters context and event. The link below shows the structure for the parameter event.
https://docs.aws.amazon.com/lex/latest/dg/lambda-input-response-format.html
I copy it here too:
{
"currentIntent": {
"name": "intent-name",
"slots": {
"slot name": "value",
"slot name": "value"
},
"slotDetails": {
"slot name": {
"resolutions" : [
{ "value": "resolved value" },
{ "value": "resolved value" }
],
"originalValue": "original text"
},
"slot name": {
"resolutions" : [
{ "value": "resolved value" },
{ "value": "resolved value" }
],
"originalValue": "original text"
}
},
"confirmationStatus": "None, Confirmed, or Denied (intent confirmation, if configured)"
},
"bot": {
"name": "bot name",
"alias": "bot alias",
"version": "bot version"
},
"userId": "User ID specified in the POST request to Amazon Lex.",
"inputTranscript": "Text used to process the request",
"invocationSource": "FulfillmentCodeHook or DialogCodeHook",
"outputDialogMode": "Text or Voice, based on ContentType request header in runtime API request",
"messageVersion": "1.0",
"sessionAttributes": {
"key": "value",
"key": "value"
},
"requestAttributes": {
"key": "value",
"key": "value"
}
}
However, when I print out the content of this parameter I see only the slot and I can access to its value directly, first level.
START RequestId: 60a541d8-b3c8-48b0-a7a3-6b3f65d96482 Version: $LATEST
{
"slotTwo": "some text here"
}
The test from the lambda console works fine. It is able to retrieve the value and continue the logic.
However when I test the bot from Lex it does not work.
Any idea why?
Thanks so much
Ester
My bad.
I missed that the values indicated in the lamdda test must correspond to the whole JSON structure. So now I added this as event test:
{
"currentIntent": {
"name": "intent-name",
"slots": {
"slotTwo": "Hi from the sendmessagetest"
}
}
}
and I access the slot within the lambda this way:
messagetext = event['currentIntent'] ['slots'] ['slotTwo']
Feel free to delete my post if you find it confusing.
Thanks everyone

What is in the JSON payload Cloudwatch sends to SNS? How can I read that data?

I'm trying to parse data with Lambda when an alarm is triggered in Cloudwatch. I am currently using SNS to trigger the lambda; however, I want to know what data is being sent to that Lambda so that I can parse it correctly.
How can I read the JSON alarm data that's passed into Lambda by SNS?
The simplest and the most accurate way is to print(event) and actually see what's inside the payload, the official AWS doco provides the following structure for SNS event Amazon SNS Sample Event, reference
{
"Records": [
{
"EventVersion": "1.0",
"EventSubscriptionArn": eventsubscriptionarn,
"EventSource": "aws:sns",
"Sns": {
"SignatureVersion": "1",
"Timestamp": "1970-01-01T00:00:00.000Z",
"Signature": "EXAMPLE",
"SigningCertUrl": "EXAMPLE",
"MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e",
"Message": "Hello from SNS!",
"MessageAttributes": {
"Test": {
"Type": "String",
"Value": "TestString"
},
"TestBinary": {
"Type": "Binary",
"Value": "TestBinary"
}
},
"Type": "Notification",
"UnsubscribeUrl": "EXAMPLE",
"TopicArn": topicarn,
"Subject": "TestInvoke"
}
}
]
}
also, this CloudWatch specific example could be useful
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:eu-west-1:000000000000:cloudwatch-alarms:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"Sns": {
"Type": "Notification",
"MessageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"TopicArn": "arn:aws:sns:eu-west-1:000000000000:cloudwatch-alarms",
"Subject": "ALARM: \"Example alarm name\" in EU - Ireland",
"Message": "{\"AlarmName\":\"Example alarm name\",\"AlarmDescription\":\"Example alarm description.\",\"AWSAccountId\":\"000000000000\",\"NewStateValue\":\"ALARM\",\"NewStateReason\":\"Threshold Crossed: 1 datapoint (10.0) was greater than or equal to the threshold (1.0).\",\"StateChangeTime\":\"2017-01-12T16:30:42.236+0000\",\"Region\":\"EU - Ireland\",\"OldStateValue\":\"OK\",\"Trigger\":{\"MetricName\":\"DeliveryErrors\",\"Namespace\":\"ExampleNamespace\",\"Statistic\":\"SUM\",\"Unit\":null,\"Dimensions\":[],\"Period\":300,\"EvaluationPeriods\":1,\"ComparisonOperator\":\"GreaterThanOrEqualToThreshold\",\"Threshold\":1.0}}",
"Timestamp": "2017-01-12T16:30:42.318Z",
"SignatureVersion": "1",
"Signature": "Cg==",
"SigningCertUrl": "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.pem",
"UnsubscribeUrl": "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:000000000000:cloudwatch-alarms:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"MessageAttributes": {}
}
}
]
}
The actual payload sent by Cloudwatch to SNS,
{
"AlarmName": "Example alarm name",
"AlarmDescription": "Example alarm description.",
"AWSAccountId": "000000000000",
"NewStateValue": "ALARM",
"NewStateReason": "Threshold Crossed: 1 datapoint (10.0) was greater than or equal to the threshold (1.0).",
"StateChangeTime": "2017-01-12T16:30:42.236+0000",
"Region": "EU - Ireland",
"OldStateValue": "OK",
"Trigger": {
"MetricName": "DeliveryErrors",
"Namespace": "ExampleNamespace",
"Statistic": "SUM",
"Unit": null,
"Dimensions": [],
"Period": 300,
"EvaluationPeriods": 1,
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"Threshold": 1
}
}
If you want to read this event in Lambda,
import json
def lambda_handler(event, context):
alert_message = json.loads(json.dumps(event))["Records"][0]["Sns"]["Message"]
return {
'statusCode': 200,
'body': json.dumps(alert_message)
}
The easiest and simple way is to create a new subscription for the SNS Topic to which the cloudwatch sends alarm data, with Email-JSON protocol and enter your email and create subscription.
Confirm the subscription by clicking on the verification link in email. When CloudWatch sends an alarm next time, you will get the JSON data in email and then you can figure out how to parse it correctly.

Using Slack Bot Dialogs with Python and AWS Lambda

I've been working on an integration to create a dialog for a Slack bot that uses python 3, AWS API Gateway, and AWS Lambda. I'm currently working on the Slack side for integration versus the end purpose of my function.
I can get my bot running with some responses if I remove the dialog and just have it chat with the user from the tutorials here and here, however once I add a dialog into the mix the bot no longer responds. I've tried basing my code off the the example code from the Slack API's github but it doesn't help. I would appreciate any guidance on how to do this using python 3.
Code below:
import os
import logging
import urllib
import boto3
from slackclient import SlackClient
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.DEBUG)
# Grab the Bot OAuth token from the environment + slack verification
BOT_TOKEN = os.environ["BOT_TOKEN"]
SLACK_VERIFICATION_TOKEN = os.environ["SLACK_VERIFICATION_TOKEN"]
slack_client = SlackClient(BOT_TOKEN)
SLACK_URL = "https://slack.com/api/dialog.open"
def lambda_handler(data, context):
''' Entry point for API Gateway '''
slack_event = data['event']
if "bot_id" in slack_event:
logging.warn("Ignore bot event")
else:
channel_id = slack_event["channel"]
support_form = slack_client.api_call(
"dialog.open",
trigger_id = slack_event["trigger_id"],
dialog = {
"title": "AWS Support Ticket",
"submit_label": "Submit",
"callback_id": "support_form",
"elements": [
{
"label": "Subject",
"type": "text",
"name": "subject",
"placeholder": "Support Case Subject"
},
{
"label": "Description",
"type": "textarea",
"name": "body",
"placeholder": "Describe the issue you would like to open a support case about"
},
{
"type": "select",
"label": "What is your issue type?",
"name": "issueType",
"options": [
{
"label": "Customer Service",
"value": "customerservice"
},
{
"label": "Technical",
"value": "technical"
}
]
},
{
"label": "What is your severity level?",
"type": "select",
"name": "serverity",
"options": [
{
"label": "5 - General Guidance",
"value": "5"
},
{
"label": "4 - System Impaired",
"value": "4"
},
{
"label": "3 - Production System Impaired",
"value": "3"
},
{
"label": "2 - Production System Down",
"value": "2"
},
{
"label": "1 - Business-critical System Down",
"value": "1"
}
]
},
{
"label": "Service Code",
"type": "text",
"name": "serviceCode"
},
{
"label": "Category Code",
"type": "text",
"name": "categoryCode"
},
{
"label": "Please choose your language",
"type": "select",
"name": "language",
"options": [
{
"label": "English",
"value": "english"
},
{
"label": "Japanese",
"value": "japanese"
}
]
},
{
"label": "What is your attachement set id?",
"type": "text",
"name": "attachementSetId"
},
{
"label": "Please enter the emails you want cc'd on this case:",
"type": "textarea",
"name": "ccEmailAddresses"
}
]
}
})
data = urllib.parse.urlencode(
(
("token", BOT_TOKEN),
("channel", channel_id),
("dialog", support_form)
)
)
# Construct the HTTP request that will be sent to the Slack API.
request = urllib.request.Request(
SLACK_URL,
data=data,
method="POST"
)
# Add a header mentioning that the text is URL-encoded.
request.add_header(
"Content-Type",
"application/x-www-form-urlencoded"
)
# Fire off the request!
urllib.request.urlopen(request).read()
# Everything went fine.
return "200 OK"

Slack chat.postMessage attachment gives no_text

When I try to add an attachment to my message I either only get the text or if I leave out the text I get "error": "no_text", is there any way to send an attachmet with chat.postMessage?
This is the python code I use for sending the message:
r = requests.post('https://slack.com/api/chat.postMessage', params=json.loads("""
{
"token": "xoxp-mytokenhere",
"channel": "C4mychannelhere",
"attachments": [
{
"text": "Question?",
"fallback": "Question?",
"callback_id": "callback_id",
"color": "#3AA3E3",
"attachment_type": "default",
"actions": [
{
"name": "question",
"text": "Yes",
"style": "good",
"type": "button",
"value": "yes"
},
{
"name": "question",
"text": "Nope",
"style": "good",
"type": "button",
"value": "no"
}
]
}
]
}
"""))
Based on the comment I went with the following solution:
r = requests.post('https://slack.com/api/chat.postMessage', params=json.loads({
"token": "xoxp-mytokenhere",
"channel": "C4mychannelhere",
"attachments": json.dumps([
{
"text": "Question?",
"fallback": "Question?",
"callback_id": "callback_id",
"color": "#3AA3E3",
"attachment_type": "default",
"actions": [
{
"name": "question",
"text": "Yes",
"style": "good",
"type": "button",
"value": "yes"
},
{
"name": "question",
"text": "Nope",
"style": "good",
"type": "button",
"value": "no"
}
]
}
])
}))
It looks like you're trying to send a JSON string as your entire set of parameters to chat.postMessage.
chat.postMessage and other web API methods only support URL-encoded query or POST body parameters, so your fields like token and channel and attachments are sent as application/x-www-form-urlencoded key/value pairs instead.
To complicate things a little further, the attachments parameter actually does take a string of URL-encoded JSON data. Your JSON array needs to be URL-encoded and stuffed into that parameter.
Depending on your goals, you could either skip using json.loads and just pass that JSON string as your attachments parameter and requests will take care of URL-encoding it for you -- or you can use something like json.dump on a native Python array you build with the same attributes.

Categories

Resources