Using Slack Bot Dialogs with Python and AWS Lambda - python

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"

Related

How to trigger Modal in an event within slack api's?

I'd like to create a modal that instantly pops up when a new user joins the channel. My thought process was to use an #app.event to trigger when a new member joins, then somehow activate my slash command. But unfortunately app.event doesn't have a trigger_id so i can't just create a modal in an event method. I'm not married to using a slash command either, but it was all I found that could get users to use checkboxes and submit the checked responses for a modal. Any help connecting the two would be great, or other suggestions would be appreciated.
#app.event("member_joined_channel")
def modal_event(event, say, body):
usr_id = event["user"],
user_id = usr_id[0]
channel_id = event["channel"]
pprint.pprint(body)
say(text=f"Welcome to the channel, <#{user_id}>! 🎉 You can introduce yourself in this channel.")
app.client.chat_postMessage(
channel=channel_id,
)
#app.command("/cmd")
def modal(body):
pprint.pprint(body)
result = app.client.views_open(
trigger_id=body['trigger_id'],
view={
"title": {
"type": "plain_text",
"text": "My App",
"emoji": True
},
"submit": {
"type": "plain_text",
"text": "Submit",
"emoji": True
},
"type": "modal",
"close": {
"type": "plain_text",
"text": "Cancel",
"emoji": True
},
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hello, Assistant to the Regional Manager Dwight! *Michael Scott* wants to know where you'd like to take the Paper Company investors to dinner tonight.\n\n"
}
},
{
"type": "input",
"element": {
"type": "checkboxes",
"options": [
{
"text": {
"type": "plain_text",
"text": "Gary Danko",
"emoji": True
},
"value": "value-0"
},
{
"text": {
"type": "plain_text",
"text": "Chipotle",
"emoji": True
},
"value": "value-1"
},
{
"text": {
"type": "plain_text",
"text": "Slack Cafe",
"emoji": True
},
"value": "value-2"
}
]
},
"label": {
"type": "plain_text",
"text": "Please select all restaurants you'd be willing to eat at:",
"emoji": True
}
}
]
}
)
Unfortunately, you need a trigger_id to open a modal. I don't know of a way you can open one without it. What you could do is listen for the [member_joined_channel][1]event and then have your app send the user an ephemeral message in the channel prompting them to click a button which would then open a modal.

AWS Lex: sending a response from lambda function with python

I am having trouble with sending a JSON response from my python3.8 lambda function (default lambda_handler function). I am pretty sure I understand what I am doing after reading most of the docs and the Lambda Function Input Event and Response Format. from that resource, it says the only required section is the 'dialogAction' section.
Right now, my lex-bot has 1 intent and one slot. I know that this works because when I add a logger to the code, I can see that my lambda function is recieving confirmed JSON format.
My code tries to send a final response from the lambda function, but when I run the lex-bot in the console I get the following error:
Invalid Lambda Response: Received invalid response from Lambda: Can not construct instance of IntentResponse, problem: The validated object is null at [Source: {"dialogAction": {"type": "Close", "fulfillmentState": "Fulfilled", "message": {"contentType": "PlainText", "content": "milk"}}}; line: 1, column: 128]
Here is my python code:
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
def lambda_handler(event, context):
# print
item = event["sessionState"]["intent"]["slots"]["MilkProduct"]["value"]["resolvedValues"][0]
logger.debug(item)
return{
"dialogAction": {
"type": "Close",
"fulfillmentState": "Fulfilled",
"message": {
"contentType": "PlainText",
"content": item
}
}
}
I do not think this is necessary for you to see, but here is what the lex-bot is sending me after it confirms the slot for the intent has been confirmed:
{
"sessionId": "120304235774457",
"inputTranscript": "I want to buy milk",
"interpretations": [
{
"intent": {
"slots": {
"MilkProduct": {
"shape": "Scalar",
"value": {
"originalValue": "milk",
"resolvedValues": [
"milk"
],
"interpretedValue": "milk"
}
}
},
"confirmationState": "None",
"name": "BuyCream",
"state": "ReadyForFulfillment"
},
"nluConfidence": 1
},
{
"intent": {
"slots": {},
"confirmationState": "None",
"name": "FallbackIntent",
"state": "ReadyForFulfillment"
}
}
],
"responseContentType": "text/plain; charset=utf-8",
"invocationSource": "FulfillmentCodeHook",
"messageVersion": "1.0",
"sessionState": {
"intent": {
"slots": {
"MilkProduct": {
"shape": "Scalar",
"value": {
"originalValue": "milk",
"resolvedValues": [
"milk"
],
"interpretedValue": "milk"
}
}
},
"confirmationState": "None",
"name": "BuyCream",
"state": "ReadyForFulfillment"
},
"originatingRequestId": "417dff57-5260-45cc-81a7-06df13fbee9a"
},
"inputMode": "Text",
"bot": {
"aliasId": "TSTALIASID",
"aliasName": "TestBotAlias",
"name": "Shopping",
"version": "DRAFT",
"localeId": "en_US",
"id": "JTGNDOEVQG"
}
}
Can someone please tell me what I am doing wrong? I have been at this for hours and I seriously do not know what I am doing wrong.
Thanks

Loading JSON from a Beautifulsoup Object

I am currently making a scraper app, but before going full out with the app, using other frameworks like Discord.py, I had to first scrape the site first. It proved quite difficult to scrape the site. The site that I am trying to scrape from is Fiverr. Anyways, long story short, I had to get some cookies to login with Python Requests. The big issue now is that the data I need to scrape comes in the form of JSON, which I don't know much about. I managed to select the javascript in question, but once I load it it gives an error: "TypeError: the JSON object must be str, bytes or bytearray, not Tag". I specifically need the "rows" part which is part of the JSON data.
I'm not quite certain how to fix this and have read and tried some similar questions here. I will appreciate any help.
import requests
from bs4 import BeautifulSoup
import re
import json
# Irrelevant to the question
class JobClass:
def __init__(self, date=None, buyer=None, request=None, duration=None, budget=None, link="https://www.fiverr.com/users/myusername/requests", id=None):
self.date = date
self.buyer = buyer
self.request = request
self.duration = duration
self.budget = budget
self.link = link
self.id = id
# Irrelevant to the question
duplicateSet = set()
scrapedSet = set()
jobObjArr = []
headers = {
# Some private cookies. To get them you just need to use a site like https://curl.trillworks.com/ it is really a life saver
# This is used to tell the site who you are to be logged in (which is why I deleted this part out of the code)
}
# Please note that I used "myusername" in the URL. This is going to be different depending on user
# Using the requests module, we use the "get" function
# provided to access the webpage provided as an
# argument to this function:
result = requests.get(
'https://www.fiverr.com/users/myusername/requests', headers=headers)
# Now, let us store the page content of the website accessed
# from requests to a variable:
src = result.content
# Now that we have the page source stored, we will use the
# BeautifulSoup module to parse and process the source.
# To do so, we create a BeautifulSoup object based on the
# source variable we created above:
soup = BeautifulSoup(src, "lxml")
data = soup.select("[type='text/javascript']")[1]
print(data)
# TypeError: the JSON object must be str, bytes or bytearray, not Tag
jsonObject = json.loads(data)
# Here is the output of print(data):
<script type="text/javascript">
document.viewData = {
"dds": {
"subCats": {
"current": {
"text": "All Subcategories",
"val": "-1"
},
"options": [{
"text": "Web \u0026 Mobile Design",
"val": 151
}, {
"text": "Web Programming",
"val": 140
}]
}
},
"results": {
"rows": [{
"type": "none",
"identifier": "5cf132b55e08360011efe633",
"cells": [{
"text": "May 31, 2019",
"type": "date",
"withText": true
}, {
"userPict": "\u003cspan class=\"missing-image-user \"\u003ec\u003c/span\u003e",
"type": "profile-40",
"cssClass": "height95"
}, {
"hintBottom": false,
"text": "My website was hacked and deleted. Need to have it recreated ",
"type": "text-wide",
"tags": [],
"attachment": false
}, {
"text": 1,
"type": "applications",
"alignCenter": true
}, {
"text": "3 days",
"type": "hidden-action",
"actionVisible": false,
"alignCenter": true,
"withText": true,
"buttons": [{
"type": "span",
"text": "3 days",
"class": "duration"
}, {
"type": "button",
"text": "Remove Request",
"class": "remove-request js-remove-request",
"meta": {
"requestId": "5cf132b55e08360011efe633",
"isProfessional": false
}
}]
}, {
"text": "---",
"type": "hidden-action",
"actionVisible": false,
"alignCenter": true,
"withText": true,
"buttons": [{
"type": "span",
"text": "---",
"class": "budget"
}, {
"type": "button",
"text": "Send Offer",
"class": "btn-standard btn-green-grad js-send-offer",
"meta": {
"username": "conto217",
"category": 3,
"subCategory": 151,
"requestId": "5cf132b55e08360011efe633",
"requestText": "My website was hacked and deleted. Need to have it recreated ",
"userPict": "\u003cspan class=\"missing-image-user \"\u003ec\u003c/span\u003e",
"isProfessional": false,
"buyerId": 32969684
}
}]
}]
}, {
"type": "none",
"identifier": "5cf12f641b6e99000edf1b60",
"cells": [{
"text": "May 31, 2019",
"type": "date",
"withText": true
}, {
"userPict": "\u003cimg src=\"https://fiverr-res.cloudinary.com/t_profile_small,q_auto,f_auto/attachments/profile/photo/648ceb417a85844b25e8bf070a70d9a0-254781561534997516.9743/MyFileName\" alt=\"muazamkhokher\" width=\"40\" height=\"40\"\u003e",
"type": "profile-40",
"cssClass": "height95"
}, {
"hintBottom": false,
"text": "Need mobile ui/ux designer from marvel wireframes",
"type": "text-wide",
"tags": [],
"attachment": false
}, {
"text": 4,
"type": "applications",
"alignCenter": true
}, {
"text": "5 days",
"type": "hidden-action",
"actionVisible": false,
"alignCenter": true,
"withText": true,
"buttons": [{
"type": "span",
"text": "5 days",
"class": "duration"
}, {
"type": "button",
"text": "Remove Request",
"class": "remove-request js-remove-request",
"meta": {
"requestId": "5cf12f641b6e99000edf1b60",
"isProfessional": false
}
}]
}, {
"text": "$50",
"type": "hidden-action",
"actionVisible": false,
"alignCenter": true,
"withText": true,
"buttons": [{
"type": "span",
"text": "$50",
"class": "budget"
}, {
"type": "button",
"text": "Send Offer",
"class": "btn-standard btn-green-grad js-send-offer",
"meta": {
"username": "muazamkhokher",
"category": 3,
"subCategory": 151,
"requestId": "5cf12f641b6e99000edf1b60",
"requestText": "Need mobile ui/ux designer from marvel wireframes",
"userPict": "\u003cimg src=\"https://fiverr-res.cloudinary.com/t_profile_small,q_auto,f_auto/attachments/profile/photo/648ceb417a85844b25e8bf070a70d9a0-254781561534997516.9743/MyFileName\" alt=\"muazamkhokher\" width=\"100\" height=\"100\"\u003e",
"isProfessional": false,
"buyerId": 25478156
}
}]
}]
....
I expect the JSON to be loaded in jsonObject, but I get an error: "TypeError: the JSON object must be str, bytes or bytearray, not Tag"
Edit: Here is some code at the end of the print statement. It randomly cuts off for some reason with no ending script tag:
}, {
"type": "none",
"identifier": "5cf1236a959aa5000f1ce094",
"cells": [{
"text": "May 31, 2019",
"type": "date",
"withText": true
}, {
"userPict": "\u003cimg src=\"https://fiverr-res.cloudinary.com/t_profile_small,q_auto,f_auto/profile/photos/30069758/original/Universalco_2a_Cloud.png\" alt=\"clarky2000\" width=\"40\" height=\"40\"\u003e",
"type": "profile-40",
"cssClass": "height95"
}, {
"hintBottom": false,
"text": "Slider revolution slider. 3 slides for a music festival. I can supply a copy what each slide should look like (see attached) and all the individual objects. Anyone can create basic RS slides, but I want this to be dynamic as its for a music festival. We are using the free version of RS if were are required to use the paid version of SL for addons please let us know. Bottom line this must be 3 dynamic slides (using the same background) for a music festival audience. Unlimited revisions is a must.",
"type": "see-more",
"tags": [{
"text": "Graphic UI"
}, {
"text": "Landing Pages"
}],
"attachment": {
"url": "/download/file/1559260800%2Fgig_requests%2Fattachment_f2a5f51b9fb473e8fc7f498929f39e3f",
"name": "Outwith Rotator_1920x1080_1.jpg",
"size": "2.68 MB"
}
}, {
"text": 2,
"type": "applications",
"alignCenter": true
}, {
"text": "24 hours",
"type": "hidden-action",
"actionVisible": false,
"alignCenter": true,
"withText": true,
"buttons": [{
"type": "span",
"text": "24 hours",
"class": "duration"
}, {
"type": "button",
"text": "Remove Request",
"class": "remove-request js-remove-request",
"meta": {
"requestId": "5cf1236a959aa5000f1ce094",
"isProfessional": false
}
}]
}, {
"text": "$23",
"type": "hidden-action",
"actionVisible": false,
"alignCenter": true,
"withText": true,
"buttons": [{
"type": "span",
"text": "$23",
"class": "budget"
}, {
"type": "button",
"text": "Send Of

Adding an adaptive card to bot framework with python

I am playing a little bit with the samples of the bot framework in python from here https://github.com/Microsoft/botbuilder-python
Now I want to add a simple adaptive card to the response which I believe it is the part where it says await context.send_activity(response) but I can not attach the card. I grabbed the card from the docs sample:
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Publish Adaptive Card schema",
"weight": "bolder",
"size": "medium"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "Image",
"url": "https://pbs.twimg.com/profile_images/3647943215/d7f12830b3c17a5a9e4afcc370e3a37e_400x400.jpeg",
"size": "small",
"style": "person"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "Matt Hidinger",
"weight": "bolder",
"wrap": true
},
{
"type": "TextBlock",
"spacing": "none",
"text": "Created {{DATE(2017-02-14T06:08:39Z, SHORT)}}",
"isSubtle": true,
"wrap": true
}
]
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Now that we have defined the main rules and features of the format, we need to produce a schema and publish it to GitHub. The schema will be the starting point of our reference documentation.",
"wrap": true
},
{
"type": "FactSet",
"facts": [
{
"title": "Board:",
"value": "Adaptive Card"
},
{
"title": "List:",
"value": "Backlog"
},
{
"title": "Assigned to:",
"value": "Matt Hidinger"
},
{
"title": "Due date:",
"value": "Not set"
}
]
}
]
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Set due date",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Date",
"id": "dueDate"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "OK"
}
]
}
},
{
"type": "Action.ShowCard",
"title": "Comment",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"id": "comment",
"isMultiline": true,
"placeholder": "Enter your comment"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "OK"
}
]
}
}
]}
I can not find a way to attach the card to the python response.
You need to create the Attachment for the activity that is sent to the user:
ADAPTIVE_CARD_ATTACHMENT = Attachment(content_type='application/vnd.microsoft.card.adaptive',
content=ADAPTIVE_CARD)
After this, you can attach it to your response activity like this:
response.attachments = [ADAPTIVE_CARD_ATTACHMENT]
Or you could add it when you create the response:
response = Activity(type='message', attachments=[ADAPTIVE_CARD_ATTACHMENT])
Note: I left out the additional code needed to create a valid activity for brevity, you still need to add the fields such as channel_id, recipient and from_property, etc.

Python - PayPal recurring Payments - Agreement Details are not shown

In my python project I have to implement paypal recurring payments.
I have installed the paypal sdk and created a file to create a PayPal payment page, like this:
import paypalrestsdk
from paypalrestsdk import BillingPlan
from paypalrestsdk import BillingAgreement
from paypalrestsdk import Payment
import webbrowser
from urllib import parse
paypalrestsdk.configure({
'mode': 'sandbox', # sandbox or live
'client_id': <my app client id>,
'client_secret': <my app secret>})
def create_bill():
billing_plan = BillingPlan({
"name": "Plan with Regular and Trial Payment Definitions",
"description": "Plan with regular and trial payment definitions.",
"type": "INFINITE",
"payment_definitions": [
{
"name": "Regular payment definition",
"type": "REGULAR",
"frequency": "MONTH",
"frequency_interval": "1",
"amount": {
"value": "100",
"currency": "USD"
},
"cycles": "0",
"charge_models": [
{
"type": "SHIPPING",
"amount": {
"value": "10",
"currency": "USD"
}
},
{
"type": "TAX",
"amount": {
"value": "12",
"currency": "USD"
}
}
]
},
{
"name": "Trial payment definition",
"type": "TRIAL",
"frequency": "WEEK",
"frequency_interval": "5",
"amount": {
"value": "9.19",
"currency": "USD"
},
"cycles": "2",
"charge_models": [
{
"type": "SHIPPING",
"amount": {
"value": "1",
"currency": "USD"
}
},
{
"type": "TAX",
"amount": {
"value": "2",
"currency": "USD"
}
}
]
}
],
"merchant_preferences": {
"setup_fee": {
"value": "1",
"currency": "USD"
},
"return_url": "https://example.com",
"cancel_url": "https://example.com/cancel",
"auto_bill_amount": "YES",
"initial_fail_amount_action": "CONTINUE",
"max_fail_attempts": "0"
}
})
# Create billing plan
if billing_plan.create():
print("Billing Plan [%s] created successfully" % billing_plan.id)
# Activate billing plan
if billing_plan.activate():
billing_plan = BillingPlan.find(billing_plan.id)
print("Billing Plan [%s] state changed to %s" % (billing_plan.id, billing_plan.state))
return billing_plan
else:
print(billing_plan.error)
else:
print(billing_plan.error)
def create_agreement(ret_bil):
billing_agreement = BillingAgreement({
"name": "Fast Speed Agreement",
"description": "Agreement for Fast Speed Plan",
"start_date": "2018-03-29T00:37:04Z",
"plan": {
"id": str(ret_bil.id)
},
"payer": {
"payment_method": "paypal"
},
"shipping_address": {
"line1": "StayBr111idge Suites",
"line2": "Cro12ok Street",
"city": "San Jose",
"state": "CA",
"postal_code": "95112",
"country_code": "US"
}
})
if billing_agreement.create():
# Extract redirect url
for link in billing_agreement.links:
if link.method == "REDIRECT":
# Capture redirect url
redirect_url = str(link.href)
# REDIRECT USER TO redirect_url
webbrowser.open(redirect_url)
else:
print(billing_agreement.error)
if __name__ == "__main__":
create_agreement(create_bill())
But when I run the code above, Paypal starts with agreement description but I can't see the item details and description defined in the BilingPlan (I expected to see the detail about items, trial period, amount, recurrence etc)
Is there something wrong in my code? This is the first time I implement Paypal in my project; have I written my code correctly to implement recurrent payments?
So many Thanks in advance
PayPal will not show the recurring period, amount and service details.
You have to show that in your website's page and proceed to PayPal.

Categories

Resources