-
Notifications
You must be signed in to change notification settings - Fork 368
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #243 from ag1le/master
Added in-skill-purchase capability
- Loading branch information
Showing
7 changed files
with
374 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,5 +23,8 @@ | |
delegate, | ||
elicit_slot, | ||
confirm_slot, | ||
confirm_intent | ||
confirm_intent, | ||
buy, | ||
upsell, | ||
refund | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
{ | ||
"interactionModel": { | ||
"languageModel": { | ||
"invocationName": "demo", | ||
"intents": [ | ||
{ | ||
"name": "AMAZON.FallbackIntent", | ||
"samples": [] | ||
}, | ||
{ | ||
"name": "AMAZON.CancelIntent", | ||
"samples": [] | ||
}, | ||
{ | ||
"name": "AMAZON.HelpIntent", | ||
"samples": [] | ||
}, | ||
{ | ||
"name": "AMAZON.StopIntent", | ||
"samples": [] | ||
}, | ||
{ | ||
"name": "BuySkillItemIntent", | ||
"slots": [ | ||
{ | ||
"name": "ProductName", | ||
"type": "LIST_OF_PRODUCT_NAMES" | ||
} | ||
], | ||
"samples": [ | ||
"{ProductName}", | ||
"buy", | ||
"shop", | ||
"buy {ProductName}", | ||
"purchase {ProductName}", | ||
"want {ProductName}", | ||
"would like {ProductName}" | ||
] | ||
}, | ||
{ | ||
"name": "RefundSkillItemIntent", | ||
"slots": [ | ||
{ | ||
"name": "ProductName", | ||
"type": "LIST_OF_PRODUCT_NAMES" | ||
} | ||
], | ||
"samples": [ | ||
"cancel {ProductName}", | ||
"return {ProductName}", | ||
"refund {ProductName}", | ||
"want a refund for {ProductName}", | ||
"would like to return {ProductName}" | ||
] | ||
} | ||
], | ||
"types": [ | ||
{ | ||
"name": "LIST_OF_PRODUCT_NAMES", | ||
"values": [ | ||
{ | ||
"name": { | ||
"value": "monthly subscription" | ||
} | ||
}, | ||
{ | ||
"name": { | ||
"value": "start smoking" | ||
} | ||
}, | ||
{ | ||
"name": { | ||
"value": "stop smoking" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import requests | ||
from flask import json | ||
from flask_ask import logger | ||
|
||
class Product(): | ||
''' | ||
Object model for inSkillProducts and methods to access products. | ||
{"inSkillProducts":[ | ||
{"productId":"amzn1.adg.product.your_product_id", | ||
"referenceName":"product_name", | ||
"type":"ENTITLEMENT", | ||
"name":"product name", | ||
"summary":"This product has helped many people.", | ||
"entitled":"NOT_ENTITLED", | ||
"purchasable":"NOT_PURCHASABLE"}], | ||
"nextToken":null, | ||
"truncated":false} | ||
''' | ||
|
||
def __init__(self, apiAccessToken): | ||
self.token = apiAccessToken | ||
self.product_list = self.query() | ||
|
||
|
||
def query(self): | ||
# Information required to invoke the API is available in the session | ||
apiEndpoint = "https://api.amazonalexa.com" | ||
apiPath = "/v1/users/~current/skills/~current/inSkillProducts" | ||
token = "bearer " + self.token | ||
language = "en-US" #self.event.request.locale | ||
|
||
url = apiEndpoint + apiPath | ||
headers = { | ||
"Content-Type" : 'application/json', | ||
"Accept-Language" : language, | ||
"Authorization" : token | ||
} | ||
#Call the API | ||
res = requests.get(url, headers=headers) | ||
logger.info('PRODUCTS:' + '*' * 80) | ||
logger.info(res.status_code) | ||
logger.info(res.text) | ||
if res.status_code == 200: | ||
data = json.loads(res.text) | ||
return data['inSkillProducts'] | ||
else: | ||
return None | ||
|
||
def list(self): | ||
""" return list of purchasable and not entitled products""" | ||
mylist = [] | ||
for prod in self.product_list: | ||
if self.purchasable(prod) and not self.entitled(prod): | ||
mylist.append(prod) | ||
return mylist | ||
|
||
def purchasable(self, product): | ||
""" return True if purchasable product""" | ||
return 'PURCHASABLE' == product['purchasable'] | ||
|
||
def entitled(self, product): | ||
""" return True if entitled product""" | ||
return 'ENTITLED' == product['entitled'] | ||
|
||
|
||
def productId(self, name): | ||
print(self.product_list) | ||
for prod in self.product_list: | ||
if name == prod['name'].lower(): | ||
return prod['productId'] | ||
return None | ||
|
||
def productName(self, id): | ||
for prod in self.product_list: | ||
if id == prod['productId']: | ||
return prod['name'] | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import logging | ||
import os | ||
import requests | ||
|
||
from flask import Flask, json, render_template | ||
from flask_ask import Ask, request, session, question, statement, context, buy, upsell, refund, logger | ||
from model import Product | ||
|
||
app = Flask(__name__) | ||
ask = Ask(app, "/") | ||
logging.getLogger('flask_ask').setLevel(logging.DEBUG) | ||
|
||
|
||
PRODUCT_KEY = "PRODUCT" | ||
|
||
|
||
|
||
@ask.on_purchase_completed( mapping={'payload': 'payload','name':'name','status':'status','token':'token'}) | ||
def completed(payload, name, status, token): | ||
products = Product(context.System.apiAccessToken) | ||
logger.info('on-purchase-completed {}'.format( request)) | ||
logger.info('payload: {} {}'.format(payload.purchaseResult, payload.productId)) | ||
logger.info('name: {}'.format(name)) | ||
logger.info('token: {}'.format(token)) | ||
logger.info('status: {}'.format( status.code == 200)) | ||
product_name = products.productName(payload.productId) | ||
logger.info('Product name'.format(product_name)) | ||
if status.code == '200' and ('ACCEPTED' in payload.purchaseResult): | ||
return question('To listen it just say - play {} '.format(product_name)) | ||
else: | ||
return question('Do you want to buy another product?') | ||
|
||
@ask.launch | ||
def launch(): | ||
products = Product(context.System.apiAccessToken) | ||
question_text = render_template('welcome', products=products.list()) | ||
reprompt_text = render_template('welcome_reprompt') | ||
return question(question_text).reprompt(reprompt_text).simple_card('Welcome', question_text) | ||
|
||
|
||
@ask.intent('BuySkillItemIntent', mapping={'product_name': 'ProductName'}) | ||
def buy_intent(product_name): | ||
products = Product(context.System.apiAccessToken) | ||
logger.info("PRODUCT: {}".format(product_name)) | ||
buy_card = render_template('buy_card', product=product_name) | ||
productId = products.productId(product_name) | ||
if productId is not None: | ||
session.attributes[PRODUCT_KEY] = productId | ||
else: | ||
return statement("I didn't find a product {}".format(product_name)) | ||
raise NotImplementedError() | ||
return buy(productId).simple_card('Welcome', question_text) | ||
|
||
#return upsell(product,'get this great product') | ||
|
||
|
||
@ask.intent('RefundSkillItemIntent', mapping={'product_name': 'ProductName'}) | ||
def refund_intent(product_name): | ||
refund_card = render_template('refund_card') | ||
logger.info("PRODUCT: {}".format(product_name)) | ||
|
||
products = Product(context.System.apiAccessToken) | ||
productId = products.productId(product_name) | ||
|
||
if productId is not None: | ||
session.attributes[PRODUCT_KEY] = productId | ||
else: | ||
raise NotImplementedError() | ||
return refund(productId) | ||
|
||
|
||
@ask.intent('AMAZON.FallbackIntent') | ||
def fallback_intent(): | ||
return statement("FallbackIntent") | ||
|
||
|
||
@ask.session_ended | ||
def session_ended(): | ||
return "{}", 200 | ||
|
||
|
||
if __name__ == '__main__': | ||
if 'ASK_VERIFY_REQUESTS' in os.environ: | ||
verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower() | ||
if verify == 'false': | ||
app.config['ASK_VERIFY_REQUESTS'] = False | ||
app.run(debug=True) | ||
|
Oops, something went wrong.