From f32527034795032108768abddb0fb1d2d5ae0a14 Mon Sep 17 00:00:00 2001 From: Jack Stubblefield Date: Sat, 22 Oct 2022 11:17:35 -0500 Subject: [PATCH 1/7] x --- READMEs/Feature-Email-Jobs-Report | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/READMEs/Feature-Email-Jobs-Report b/READMEs/Feature-Email-Jobs-Report index f4ea4f0..72186e2 100644 --- a/READMEs/Feature-Email-Jobs-Report +++ b/READMEs/Feature-Email-Jobs-Report @@ -1,27 +1,37 @@ -# Component Name +# Email Jobs Report ### User Story - Purpose of Component -As a user I would like Alexa to give me a list of current jobs and email them to me. +As a user I would like Alexa to give me a list of current jobs and email them to me. + +### Resources used in building this -### Resources used in building this: Main Resources + +- To link external services to the alexa skill you must first follow this guide to setup IAM permissions. +- setting up the email params: +- API reference for sending the email. + Additional Resources ### Dependencies +- No dependencies required. ### Intents Utterances -### Notable Code Block w/Code Description. +- See: [Get Code Challenge](./Feature-Get-Code-Challange.md) + +### Notable Code Block w/Code Description + Link to .js file with comments for each section screenshot of the code: +### Notable Bugs and Issues that occurred -### Notable Bugs and Issues that occurred. - Problem - Solution ### Testing -### Known bugs/issues \ No newline at end of file +### Known bugs/issues From b16a12c7ee7bb395e9d739020823633011701587 Mon Sep 17 00:00:00 2001 From: Jack Stubblefield Date: Sat, 22 Oct 2022 12:33:05 -0500 Subject: [PATCH 2/7] finished email config file --- READMEs/Configure-Alexa-to-Email-User.md | 102 ++++++++++++++++++----- READMEs/Feature-Email-Jobs-Report | 6 +- READMEs/Feature-Email-Setup.md | 52 ++++++++++++ img/alexa-hosted-resources.png | Bin 0 -> 8456 bytes img/alexa-permissions.png | Bin 0 -> 7269 bytes 5 files changed, 137 insertions(+), 23 deletions(-) create mode 100644 READMEs/Feature-Email-Setup.md create mode 100644 img/alexa-hosted-resources.png create mode 100644 img/alexa-permissions.png diff --git a/READMEs/Configure-Alexa-to-Email-User.md b/READMEs/Configure-Alexa-to-Email-User.md index dea93de..7a34bf6 100644 --- a/READMEs/Configure-Alexa-to-Email-User.md +++ b/READMEs/Configure-Alexa-to-Email-User.md @@ -1,39 +1,101 @@ +# Configure Alexa to send emails + ## [Back to Table of Contents](./Table-of-Contents.md) -sendSolutionToChallenegeEmail Feature +### User Story - Purpose of Component + +- As a user I would like to recieve emails from Alexa with solutions to the code challenge I am working on. +- As a user I would like to recieve emails from Alexa with a list of daily job postings. -### +### Resources used in building this -[To link external services to the alexa skill you must first follow this guide to setup IAM permissions](https://developer.amazon.com/en-US/docs/alexa/hosted-skills/alexa-hosted-skills-personal-aws.html) +When you are creating your Alexa skill you can choose to self host or to have it "Alexa Hosted". In this application we selected "Alexa Hosted", which means she provisions certain resources that are managed by default. These services are: + - S3 + - DynamoDB + - Lambda + - Cloudwatch + +![Console](../img/alexa-hosted-resources.png) -[etting up the email params:](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) +When you go to the "Code" section in your Alexa developer console these skills will be listed at the top. All of these are available to you without any setup, with the caviat that you are granted a static role that has limited permissions. That is why to setup SES (or any other service), we need to do some setup. Check out the resource links below for the steps to allow Alexa to connect with your AWS account. -[API reference for sending the email.](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) +- [Linking other AWS resources](https://developer.amazon.com/en-US/docs/alexa/hosted-skills/alexa-hosted-skills-personal-aws.html) -[Document 2:](https://pamphl3t.medium.com/send-a-email-from-your-alexa-with-aws-ses-176a81515680) +You will also need to configure your SES to verify an email for sending. -# Component Name +- [Setting up SES](https://docs.aws.amazon.com/ses/latest/dg/Welcome.html) -### User Story - Purpose of Component +- [A basic walkthrough for sending emails](https://pamphl3t.medium.com/send-a-email-from-your-alexa-with-aws-ses-176a81515680) -### Resources used in building this: -Main Resources -Additional Resources +And you will need to enable permissions in the build tab of your developer console. + +![Permissions](../img/alexa-permissions.png) ### Dependencies -### Intents Utterances +All you need is the AWS-SDK (which Alexa should have installed by default) + +## Relevant Code Snippets + +You need to grant alexa permissions to read the user email. We did this by putting the string in an array, so more permissions could be added if needed. + +```js + const PERMISSIONS = ['alexa::profile:email:read'] + ``` + +This is how you grab a user's email from the account. Keep in mind they must enable it in the alexa app in the skill settings. [Here is an article that walks you through how to do this](https://www.c-sharpcorner.com/article/getting-an-email-address-in-an-alexa-skill2/). + +```js + + const { serviceClientFactory, responseBuilder } = handlerInput + + try { + //* fetching the user's email from the account + const upsServiceClient = serviceClientFactory.getUpsServiceClient() + const profileEmail = await upsServiceClient.getProfileEmail() + //* if there is no profile with the associated account. + if (!profileEmail) { + const noEmailResponse = `It looks like you do not have an email set. You can set your email from the alexa companion app.` + return responseBuilder.speak(noEmailResponse).getResponse() + } +``` + +This is how Alexa assumes a temporary role that you set up in your IAMS following the "Linking other AWS resources" link. You will need this to utilize SES in any way. + +```js +const sts = new aws.STS({ apiVersion: '2011-06-15' }) -### Notable Code Block w/Code Description. -Link to .js file with comments for each section + const credentials = await sts + .assumeRole( + { + RoleArn: '', + RoleSessionName: 'SendEmailRoleSession' + }, + (err, res) => { + if (err) { + console.log('AssumeRole FAILED: ', err) + throw new Error('Error while assuming role') + } + return res + } + ) + .promise() +``` -screenshot of the code: +Once you have permissions, you can use SES to send an email with the credentials you just made. +```js + const ses = new aws.SES({ + apiVersion: '2010-12-01', + region: 'us-east-2', + accessKeyId: credentials.Credentials.AccessKeyId, + secretAccessKey: credentials.Credentials.SecretAccessKey, + sessionToken: credentials.Credentials.SessionToken + }) +``` -### Notable Bugs and Issues that occurred. -- Problem -- Solution +___ -### Testing +### Known bugs/issues -### Known bugs/issues \ No newline at end of file +None are known at the time of writing this. Make sure permissions are granted in your developer console. diff --git a/READMEs/Feature-Email-Jobs-Report b/READMEs/Feature-Email-Jobs-Report index a13deaa..da3b7b4 100644 --- a/READMEs/Feature-Email-Jobs-Report +++ b/READMEs/Feature-Email-Jobs-Report @@ -10,9 +10,9 @@ As a user I would like Alexa to give me a list of current jobs and email them to Main Resources -- To link external services to the alexa skill you must first follow this guide to setup IAM permissions. -- setting up the email params: -- API reference for sending the email. +- [Linking other AWS resources](https://developer.amazon.com/en-US/docs/alexa/hosted-skills/alexa-hosted-skills-personal-aws.html) +- [Setting up the email params](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) +- [API reference for sending the email](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) Additional Resources diff --git a/READMEs/Feature-Email-Setup.md b/READMEs/Feature-Email-Setup.md new file mode 100644 index 0000000..10824c8 --- /dev/null +++ b/READMEs/Feature-Email-Setup.md @@ -0,0 +1,52 @@ +# Email Setup + +### User Story - Purpose of Component + +- As a user I would like to recieve emails from Alexa with solutions to the code challenge I am working on. +- As a user I would like to recieve emails from Alexa with a list of daily job postings. + +### Resources used in building this + +When you are creating your Alexa skill you can choose to self host or to have it "Alexa Hosted". In this application we selected "Alexa Hosted", which means she provisions certain resources that are managed by default. These services are: + - S3 + - DynamoDB + - Lambda + - Cloudwatch + +![Console](../img/alexa-hosted-resources.png) + +When you go to the "Code" section in your Alexa developer console these skills will be listed at the top. All of these are available to you without any setup, with the caviat that you are granted a static role that has limited permissions. That is why to setup SES (or any other service), we need to do some setup. Check out the resource links below for the steps to allow Alexa to connect with your AWS account. + +- [Linking other AWS resources](https://developer.amazon.com/en-US/docs/alexa/hosted-skills/alexa-hosted-skills-personal-aws.html) + +You will also need to configure your SES to verify an email for sending. + +- [Setting up SES](https://docs.aws.amazon.com/ses/latest/dg/Welcome.html) + +[A basic walkthrough for sending emails](https://pamphl3t.medium.com/send-a-email-from-your-alexa-with-aws-ses-176a81515680) + +Additional Resources + +### Dependencies + +All you need is the AWS-SDK (which Alexa should have installed by default) + +## Intents + +All of the emails are sent within [The code challenge skill](./Configure-API-for-Jobs.md) + +## Utterances + +### Notable Code Block w/Code Description + +- Link to .js file with comments for each section +- screenshot of the code: + +### Notable Bugs and Issues that occurred + +- Problem +- Solution + +### Testing + +### Known bugs/issues diff --git a/img/alexa-hosted-resources.png b/img/alexa-hosted-resources.png new file mode 100644 index 0000000000000000000000000000000000000000..ac7eb5c43ce92dedb72d523e764df8060fa0ad2a GIT binary patch literal 8456 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ⅆP9n4#l!k6Cc`rW5)~bm(+cm)eI4A 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 literal 0 HcmV?d00001 diff --git a/img/alexa-permissions.png b/img/alexa-permissions.png new file mode 100644 index 0000000000000000000000000000000000000000..336e20743240cde639b96f73f92eacef4e3b93f5 GIT binary patch literal 7269 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 literal 0 HcmV?d00001 From 45ccc999cfb6d21d24a01cf99279e468c8aa007d Mon Sep 17 00:00:00 2001 From: Jack Stubblefield Date: Sat, 22 Oct 2022 12:48:09 -0500 Subject: [PATCH 3/7] jobs report finished --- READMEs/Feature-Email-Jobs-Report | 140 ++++++++++++++++++++++++++++-- READMEs/Feature-Email-Setup.md | 52 ----------- 2 files changed, 133 insertions(+), 59 deletions(-) delete mode 100644 READMEs/Feature-Email-Setup.md diff --git a/READMEs/Feature-Email-Jobs-Report b/READMEs/Feature-Email-Jobs-Report index da3b7b4..3467fcd 100644 --- a/READMEs/Feature-Email-Jobs-Report +++ b/READMEs/Feature-Email-Jobs-Report @@ -10,6 +10,8 @@ As a user I would like Alexa to give me a list of current jobs and email them to Main Resources +[MAKE SURE YOU CONFIGURE YOUR ALEXA FIRST](./Configure-Alexa-to-Email-User.md) + - [Linking other AWS resources](https://developer.amazon.com/en-US/docs/alexa/hosted-skills/alexa-hosted-skills-personal-aws.html) - [Setting up the email params](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) - [API reference for sending the email](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) @@ -19,21 +21,145 @@ Additional Resources ### Dependencies - No dependencies required. + ### Intents Utterances -- See: [Get Code Challenge](./Feature-Get-Code-Challange.md) +- See: [Configuring the API for jobs](./Configure-API-for-Jobs.md) ### Notable Code Block w/Code Description -Link to .js file with comments for each section - -screenshot of the code: +```js +// 1. Assume the role with STS +const sts = new aws.STS({ apiVersion: '2011-06-15' }) + const credentials = await sts + .assumeRole( + { + RoleArn: 'YOUR ALEXA ROLE ARN', + RoleSessionName: 'SendEmailRoleSession' + }, + (err, res) => { + if (err) { + console.log('AssumeRole FAILED: ', err) + throw new Error('Error while assuming role') + } + return res + } + ) + .promise() + // 2. Jobs are fetched + let response = await logic.fetchJobsApi() + addS3Object('dailyJobsReports', response.data) + let strings = '' + response.data.map((job, index) => { + if (index < 3) { + return (strings += `${job.employer_name} as a ${job.job_title}, `) + } + }) + // 3. Grabbing session storage to remember what jobs were fetched + const sessionAttributes = handlerInput.attributesManager.getSessionAttributes() + + // 4. Grabbing email from Alexa users account + const { serviceClientFactory, responseBuilder } = handlerInput + + + try { + //* fetching the user's email from the account + const upsServiceClient = serviceClientFactory.getUpsServiceClient() + const profileEmail = await upsServiceClient.getProfileEmail() + //* if there is no profile with the associated account. + if (!profileEmail) { + const noEmailResponse = `It looks like you do not have an email set. You can set your email from the alexa companion app.` + return responseBuilder.speak(noEmailResponse).getResponse() + } + + //* 5. Make a new SES object with the assumed role credentials + + //* create a new SES instance + const ses = new aws.SES({ + apiVersion: '2010-12-01', + region: 'us-east-2', + accessKeyId: credentials.Credentials.AccessKeyId, + secretAccessKey: credentials.Credentials.SecretAccessKey, + sessionToken: credentials.Credentials.SessionToken + }) + + //* async function to send the email and wait for a promise to be returned + /** + * @param {string} reciever The email address you want to recieve the data at + * @param {string} body The email body, the actual text inside the email. NOT HTML. //! Will be an object once feature is implemented. + * @param {string} subject The subject line of the email. + * @returns + */ + const sendEmail = async (reciever, body, subject) => { + let params = { + Destination: { + ToAddresses: [reciever] + }, + Message: { + Body: { + Html: { + Charset: 'UTF-8', + Data: `

Tech Prep!

+

Thank you for using our Alexa Skill!

+

Here are the job postings that you requested:

+ ${body.map(job => { + return `
    +

    ${job.job_title}

    +

    ${job.employer_name}

    +

    ${job.job_description}

    + Apply Here! +
` + })} + ` + } + }, + + Subject: { Data: subject } + }, + Source: 'YOUR SES VERIFIED EMAIL' + } + + return await ses.sendEmail(params).promise() + } + + //* send the email with the specified parameters + sendEmail(profileEmail, response.data, 'Job Listings from TechPrep') + + //! if there are improper permissions. Sometimes they can be switched off if the skill is rebuilt/redeployed. + } catch (error) { + console.log( + JSON.stringify('403 *******************************************', error) + ) + if (error.statusCode === 403) { + return responseBuilder + .speak(messages.NOTIFY_MISSING_PERMISSIONS) + .withAskForPermissionsConsentCard(PERMISSIONS) + .getResponse() + } + console.log(JSON.stringify(error)) + const response = responseBuilder.speak(messages.ERROR).getResponse() + return response + } + + let speakOutput = ` I found a list of jobs for you, here are the first three, ${strings} I emailed you the full list of jobs. ` + + return handlerInput.responseBuilder + .speak(speakOutput) + .reprompt() + .getResponse() +``` ### Notable Bugs and Issues that occurred -- Problem -- Solution +- Problem: Emails being recieved as undefined +- Solution: Make sure your job data is passed into the sendEmail function correctly. -### Testing +- Problem: Permissions issues +- Solution: + - 1. Make sure your ARN's are correct + - 2. Make sure your role is configured properly + - 3. Make sure your SES verified email is EXACTLY the same as it is in AWS. Even a capital letter will break it. ### Known bugs/issues + +No known bugs at the time of writing this. Sometimes emails are sent to spam or junk. \ No newline at end of file diff --git a/READMEs/Feature-Email-Setup.md b/READMEs/Feature-Email-Setup.md deleted file mode 100644 index 10824c8..0000000 --- a/READMEs/Feature-Email-Setup.md +++ /dev/null @@ -1,52 +0,0 @@ -# Email Setup - -### User Story - Purpose of Component - -- As a user I would like to recieve emails from Alexa with solutions to the code challenge I am working on. -- As a user I would like to recieve emails from Alexa with a list of daily job postings. - -### Resources used in building this - -When you are creating your Alexa skill you can choose to self host or to have it "Alexa Hosted". In this application we selected "Alexa Hosted", which means she provisions certain resources that are managed by default. These services are: - - S3 - - DynamoDB - - Lambda - - Cloudwatch - -![Console](../img/alexa-hosted-resources.png) - -When you go to the "Code" section in your Alexa developer console these skills will be listed at the top. All of these are available to you without any setup, with the caviat that you are granted a static role that has limited permissions. That is why to setup SES (or any other service), we need to do some setup. Check out the resource links below for the steps to allow Alexa to connect with your AWS account. - -- [Linking other AWS resources](https://developer.amazon.com/en-US/docs/alexa/hosted-skills/alexa-hosted-skills-personal-aws.html) - -You will also need to configure your SES to verify an email for sending. - -- [Setting up SES](https://docs.aws.amazon.com/ses/latest/dg/Welcome.html) - -[A basic walkthrough for sending emails](https://pamphl3t.medium.com/send-a-email-from-your-alexa-with-aws-ses-176a81515680) - -Additional Resources - -### Dependencies - -All you need is the AWS-SDK (which Alexa should have installed by default) - -## Intents - -All of the emails are sent within [The code challenge skill](./Configure-API-for-Jobs.md) - -## Utterances - -### Notable Code Block w/Code Description - -- Link to .js file with comments for each section -- screenshot of the code: - -### Notable Bugs and Issues that occurred - -- Problem -- Solution - -### Testing - -### Known bugs/issues From 5aa63f670823b95e4f10f6ab1cfa8e43dbdfe0e3 Mon Sep 17 00:00:00 2001 From: Jack Stubblefield Date: Sat, 22 Oct 2022 12:57:07 -0500 Subject: [PATCH 4/7] finished email code challenge solution --- .../Feature-Email-Code-Challenge-Solution.md | 120 ++++++++++++++++-- ...bs-Report => Feature-Email-Jobs-Report.md} | 0 2 files changed, 109 insertions(+), 11 deletions(-) rename READMEs/{Feature-Email-Jobs-Report => Feature-Email-Jobs-Report.md} (100%) diff --git a/READMEs/Feature-Email-Code-Challenge-Solution.md b/READMEs/Feature-Email-Code-Challenge-Solution.md index 55fe67e..58c8cbb 100644 --- a/READMEs/Feature-Email-Code-Challenge-Solution.md +++ b/READMEs/Feature-Email-Code-Challenge-Solution.md @@ -1,27 +1,125 @@ -# Email Code Challenge Solution to User +# Email Code Challenge Solution ## [Back to Table of Contents](./Table-of-Contents.md) ### User Story - Purpose of Component -### Resources used in building this: +As a user I would like Alexa to give me the solution to the code challenge I completed. + +### Resources used in building this + Main Resources -Additional Resources + +[MAKE SURE YOU CONFIGURE YOUR ALEXA FIRST](./Configure-Alexa-to-Email-User.md) + +- [Linking other AWS resources](https://developer.amazon.com/en-US/docs/alexa/hosted-skills/alexa-hosted-skills-personal-aws.html) +- [Setting up the email params](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) +- [API reference for sending the email](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/) + +Additional Resources: ### Dependencies +- No dependencies required. + ### Intents Utterances -### Notable Code Block w/Code Description. -Link to .js file with comments for each section +- See: [Get a code challenge](./Feature-Get-Code-Challange.md) + +### Notable Code Block w/Code Description + +Most of this is the same as the [Email Job report](./Feature-Email-Jobs-Report.md). Modify it to your needs. The source code may look confusing because it is built around the functionality of the timer feature. + +```js +//* making the request to send an email with Alexa credentials + // 1. Assume the AWS resource role using STS AssumeRole Action + + const sts = new aws.STS({ apiVersion: '2011-06-15' }) + + const credentials = await sts + .assumeRole( + { + RoleArn: 'ALEXA ROLE ARN', + RoleSessionName: 'SendEmailRoleSession' + }, + (err, res) => { + if (err) { + console.log('AssumeRole FAILED: ', err) + throw new Error('Error while assuming role') + } + return res + } + ) + .promise() + + //* 2. Make a new SES object with the assumed role credentials + + //* create a new SES instance + const ses = new aws.SES({ + apiVersion: '2010-12-01', + region: 'us-east-1', + accessKeyId: credentials.Credentials.AccessKeyId, + secretAccessKey: credentials.Credentials.SecretAccessKey, + sessionToken: credentials.Credentials.SessionToken + }) + + //* async function to send the email and wait for a promise to be returned + /** + * + * @param {string} reciever The email address you want to recieve the data at + * @param {string} body The email body, the actual text inside the email. NOT HTML. //! Will be an object once feature is implemented. + * @param {string} subject The subject line of the email. + * @returns + */ + const sendEmail = async (reciever, body, subject) => { + let params = { + Destination: { + ToAddresses: [reciever] + }, + Message: { + Body: { + Html: { + Charset: 'UTF-8', + Data: `

Tech Prep!

+

Thank you for using our Alexa Skill!

+

Here is the code challenge that you attempted:

+ +
+

${body.chosenChallenge.description}

+ You can view the solution here +
` + } + }, + + Subject: { Data: subject } + }, + Source: 'YOUR SES VERIFIED EMAIL HERE' + } + + + return await ses.sendEmail(params).promise() + } + + //* send the email with the specified parameters + sendEmail(sessionAttributes.profileEmail, sessionAttributes, 'TechPrep Solution Code') + + return handlerInput.responseBuilder + .getResponse(); + + //! if there are improper permissions. Sometimes they can be switched off if the skill is rebuilt/redeployed. +``` -screenshot of the code: +### Notable Bugs and Issues that occurred +- Problem: Emails being recieved as undefined +- Solution: Make sure your data is passed into the sendEmail function correctly. -### Notable Bugs and Issues that occurred. -- Problem -- Solution +- Problem: Permissions issues +- Solution: + - 1. Make sure your ARN's are correct + - 2. Make sure your role is configured properly + - 3. Make sure your SES verified email is EXACTLY the same as it is in AWS. Even a capital letter will break it. -### Testing +### Known bugs/issues -### Known bugs/issues \ No newline at end of file +No known bugs at the time of writing this. Sometimes emails are sent to spam or junk. diff --git a/READMEs/Feature-Email-Jobs-Report b/READMEs/Feature-Email-Jobs-Report.md similarity index 100% rename from READMEs/Feature-Email-Jobs-Report rename to READMEs/Feature-Email-Jobs-Report.md From 776b233e0ffbd4e9e058b62e052525007a1eac9a Mon Sep 17 00:00:00 2001 From: Jack Stubblefield Date: Sat, 22 Oct 2022 13:02:05 -0500 Subject: [PATCH 5/7] moved skill code into a folder --- .../interactionModels/custom/en-US.json | 141 ++++++++++++++++++ .../lambda/ConnectionsResponseHandler.js | 2 +- .../lambda/challenges.json | 2 +- .../lambda/index.js | 2 +- Tech-Prep-Skill/lambda/local-debugger.js | 135 +++++++++++++++++ logic.js => Tech-Prep-Skill/lambda/logic.js | 2 +- .../lambda/package.json | 4 +- Tech-Prep-Skill/lambda/questions.json | 13 ++ s3.js => Tech-Prep-Skill/lambda/s3.js | 0 .../lambda/timerPayload.js | 0 util.js => Tech-Prep-Skill/lambda/util.js | 0 Tech-Prep-Skill/skill.json | 56 +++++++ questions.json | 13 -- 13 files changed, 351 insertions(+), 19 deletions(-) create mode 100644 Tech-Prep-Skill/interactionModels/custom/en-US.json rename ConnectionsResponseHandler.js => Tech-Prep-Skill/lambda/ConnectionsResponseHandler.js (98%) rename challenges.json => Tech-Prep-Skill/lambda/challenges.json (99%) rename base.index.js => Tech-Prep-Skill/lambda/index.js (99%) create mode 100644 Tech-Prep-Skill/lambda/local-debugger.js rename logic.js => Tech-Prep-Skill/lambda/logic.js (90%) rename package.json => Tech-Prep-Skill/lambda/package.json (81%) create mode 100644 Tech-Prep-Skill/lambda/questions.json rename s3.js => Tech-Prep-Skill/lambda/s3.js (100%) rename timerPayload.js => Tech-Prep-Skill/lambda/timerPayload.js (100%) rename util.js => Tech-Prep-Skill/lambda/util.js (100%) create mode 100644 Tech-Prep-Skill/skill.json delete mode 100644 questions.json diff --git a/Tech-Prep-Skill/interactionModels/custom/en-US.json b/Tech-Prep-Skill/interactionModels/custom/en-US.json new file mode 100644 index 0000000..1beba7c --- /dev/null +++ b/Tech-Prep-Skill/interactionModels/custom/en-US.json @@ -0,0 +1,141 @@ +{ + "interactionModel": { + "languageModel": { + "invocationName": "tech prep", + "intents": [ + { + "name": "AMAZON.CancelIntent", + "samples": [] + }, + { + "name": "AMAZON.HelpIntent", + "samples": [] + }, + { + "name": "AMAZON.StopIntent", + "samples": [] + }, + { + "name": "GetChallengeIntent", + "slots": [], + "samples": [ + "code problem", + "coding problem", + "code challenge", + "coding challenge", + "code", + "coding", + "code practice", + "coding practice", + "a challenge", + "technical question", + "let\u0027s do a code challenge", + "give me a code challenge", + "skip", + "skip challenge", + "next code challenge", + "next challenge" + ] + }, + { + "name": "AMAZON.NavigateHomeIntent", + "samples": [] + }, + { + "name": "AMAZON.FallbackIntent", + "samples": [] + }, + { + "name": "AMAZON.RepeatIntent", + "samples": [ + "repeat code problem", + "tell me the code problem", + "what was that code problem", + "give me the code problem again", + "repeat the code problem", + "repeat problem", + "code challenge again", + "tell me the code challenge", + "what was that code challenge", + "give me the code challenge again", + "repeat code challenge", + "repeat that", + "tell me again", + "what", + "repeat challenge", + "I didn\u0027t hear that", + "I did not get that", + "I did not hear that", + "i didn\u0027t get that", + "what was that", + "say that again", + "repeat" + ] + }, + { + "name": "GetHintIntent", + "slots": [], + "samples": [ + "hint me", + "give a hint", + "tell me a hint", + "hints", + "any hints", + "Can I have a hint", + "hint please", + "hint", + "give me a hint" + ] + }, + { + "name": "GetQuestionIntent", + "slots": [], + "samples": [ + "next question", + "get interview question", + "give interview question", + "get interview", + "give interview", + "id like to be interviewed", + "ask me an interview question", + "interview", + "interview me" + ] + }, + { + "name": "AMAZON.YesIntent", + "samples": [ + "yes please", + "sure", + "yeah", + "yes" + ] + }, + { + "name": "AMAZON.NoIntent", + "samples": [ + "no way", + "nah", + "no thanks", + "no" + ] + }, + { + "name": "FetchJobsIntent", + "slots": [], + "samples": [ + "positions", + "jobs", + "search job postings", + "search job listings", + "job", + "find me a job", + "find job listings" + ] + } + ], + "types": [] + } + }, + "version": "9" +} \ No newline at end of file diff --git a/ConnectionsResponseHandler.js b/Tech-Prep-Skill/lambda/ConnectionsResponseHandler.js similarity index 98% rename from ConnectionsResponseHandler.js rename to Tech-Prep-Skill/lambda/ConnectionsResponseHandler.js index a56be47..b1066a0 100644 --- a/ConnectionsResponseHandler.js +++ b/Tech-Prep-Skill/lambda/ConnectionsResponseHandler.js @@ -48,4 +48,4 @@ const ConnectionsResponseHandler = { } }; -module.exports = ConnectionsResponseHandler; +module.exports = ConnectionsResponseHandler; \ No newline at end of file diff --git a/challenges.json b/Tech-Prep-Skill/lambda/challenges.json similarity index 99% rename from challenges.json rename to Tech-Prep-Skill/lambda/challenges.json index f0794a9..2dd52ad 100644 --- a/challenges.json +++ b/Tech-Prep-Skill/lambda/challenges.json @@ -39,4 +39,4 @@ "hints": ["You can take an iterative approach or a recursive approach.", "Consider a counter variable to help you keep count."], "solution": "https://www.geeksforgeeks.org/find-length-of-a-linked-list-iterative-and-recursive/?ref=lbp" } -] +] \ No newline at end of file diff --git a/base.index.js b/Tech-Prep-Skill/lambda/index.js similarity index 99% rename from base.index.js rename to Tech-Prep-Skill/lambda/index.js index cff78e0..881d381 100644 --- a/base.index.js +++ b/Tech-Prep-Skill/lambda/index.js @@ -799,4 +799,4 @@ exports.handler = Alexa.SkillBuilders.custom() ) .addErrorHandlers(ErrorHandler) .withApiClient(new Alexa.DefaultApiClient()) - .lambda() \ No newline at end of file + .lambda() diff --git a/Tech-Prep-Skill/lambda/local-debugger.js b/Tech-Prep-Skill/lambda/local-debugger.js new file mode 100644 index 0000000..005d3de --- /dev/null +++ b/Tech-Prep-Skill/lambda/local-debugger.js @@ -0,0 +1,135 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/* ## DEPRECATION NOTICE + +This script has been deprecated and is no longer supported. +Please use the [ASK Toolkit for VS Code] +(https://marketplace.visualstudio.com/items?itemName=ask-toolkit.alexa-skills-kit-toolkit), +which provides a more end-to-end integration with Visual Studio Code. If you +use another editor/IDE, please check out the [ASK SDK Local Debug package at npm] +(https://www.npmjs.com/package/ask-sdk-local-debug). + +*/ + +const net = require('net'); +const fs = require('fs'); + +const localDebugger = net.createServer(); + +const httpHeaderDelimeter = '\r\n'; +const httpBodyDelimeter = '\r\n\r\n'; +const defaultHandlerName = 'handler'; +const host = 'localhost'; +const defaultPort = 0; + +/** + * Resolves the skill invoker class dependency from the user provided + * skill entry file. + */ + +// eslint-disable-next-line import/no-dynamic-require +const skillInvoker = require(getAndValidateSkillInvokerFile()); +const portNumber = getAndValidatePortNumber(); +const lambdaHandlerName = getLambdaHandlerName(); + +/** + * Starts listening on the port for incoming skill requests. + */ + +localDebugger.listen(portNumber, host, () => { + console.log(`Starting server on port: ${localDebugger.address().port}.`); +}); + +/** + * For a new incoming skill request a new socket connection is established. + * From the data received on the socket the request body is extracted, parsed into + * JSON and passed to the skill invoker's lambda handler. + * The response from the lambda handler is parsed as a HTTP 200 message format as specified + * here - https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#http-header-1 + * The response is written onto the socket connection. + */ + +localDebugger.on('connection', (socket) => { + console.log(`Connection from: ${socket.remoteAddress}:${socket.remotePort}`); + socket.on('data', (data) => { + const body = JSON.parse(data.toString().split(httpBodyDelimeter).pop()); + console.log(`Request envelope: ${JSON.stringify(body)}`); + skillInvoker[lambdaHandlerName](body, null, (_invokeErr, response) => { + response = JSON.stringify(response); + console.log(`Response envelope: ${response}`); + socket.write(`HTTP/1.1 200 OK${httpHeaderDelimeter}Content-Type: application/json;charset=UTF-8${httpHeaderDelimeter}Content-Length: ${response.length}${httpBodyDelimeter}${response}`); + }); + }); +}); + +/** + * Validates user specified port number is in legal range [0, 65535]. + * Defaults to 0. + */ + +function getAndValidatePortNumber() { + const portNumberArgument = Number(getArgument('portNumber', defaultPort)); + if (!Number.isInteger(portNumberArgument)) { + throw new Error(`Port number has to be an integer - ${portNumberArgument}.`); + } + if (portNumberArgument < 0 || portNumberArgument > 65535) { + throw new Error(`Port out of legal range: ${portNumberArgument}. The port number should be in the range [0, 65535]`); + } + if (portNumberArgument === 0) { + console.log('The TCP server will listen on a port that is free.' + + 'Check logs to find out what port number is being used'); + } + return portNumberArgument; +} + +/** + * Gets the lambda handler name. + * Defaults to "handler". + */ + +function getLambdaHandlerName() { + return getArgument('lambdaHandler', defaultHandlerName); +} + +/** + * Validates that the skill entry file exists on the path specified. + * This is a required field. + */ + +// eslint-disable-next-line consistent-return +function getAndValidateSkillInvokerFile() { + const fileNameArgument = getArgument('skillEntryFile'); + if (!fs.existsSync(fileNameArgument)) { + throw new Error(`File not found: ${fileNameArgument}`); + } + return fileNameArgument; +} + +/** + * Helper function to fetch the value for a given argument + * @param {argumentName} argumentName name of the argument for which the value needs to be fetched + * @param {defaultValue} defaultValue default value of the argument that is returned if the value doesn't exist + */ + +function getArgument(argumentName, defaultValue) { + const index = process.argv.indexOf(`--${argumentName}`); + if (index === -1 || typeof process.argv[index + 1] === 'undefined') { + if (defaultValue === undefined) { + throw new Error(`Required argument - ${argumentName} not provided.`); + } else { + return defaultValue; + } + } + return process.argv[index + 1]; +} diff --git a/logic.js b/Tech-Prep-Skill/lambda/logic.js similarity index 90% rename from logic.js rename to Tech-Prep-Skill/lambda/logic.js index ef007b4..be3359b 100644 --- a/logic.js +++ b/Tech-Prep-Skill/lambda/logic.js @@ -12,7 +12,7 @@ const options = { num_pages: '1' }, headers: { - 'X-RapidAPI-Key': '', + 'X-RapidAPI-Key': '06a16c25e6msh7182d6c514e7e7bp1047ddjsne068aa9f6a0e', 'X-RapidAPI-Host': 'jsearch.p.rapidapi.com' } }; diff --git a/package.json b/Tech-Prep-Skill/lambda/package.json similarity index 81% rename from package.json rename to Tech-Prep-Skill/lambda/package.json index df7445e..9e259ca 100644 --- a/package.json +++ b/Tech-Prep-Skill/lambda/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "author": "KC Hofstetter, Stephen Clemmer, Brandon Pitts, Jack Stubblefield and Xavier Hillman", + "author": "Amazon Alexa", "license": "Apache License", "dependencies": { "ask-sdk-core": "^2.7.0", @@ -16,4 +16,4 @@ "node-fetch": "2.6.7", "axios": "^1.1.3" } -} +} \ No newline at end of file diff --git a/Tech-Prep-Skill/lambda/questions.json b/Tech-Prep-Skill/lambda/questions.json new file mode 100644 index 0000000..6c96202 --- /dev/null +++ b/Tech-Prep-Skill/lambda/questions.json @@ -0,0 +1,13 @@ +["Tell me about a time when you had to work closely with someone whose personality was very different from yours.", + "Give me an example of a time you faced a conflict with a coworker. How did you handle that?", + "Describe a time when you had to step up and demonstrate leadership skills.", + "Give me an example of a time when you didn’t meet a client’s expectation. What happened, and how did you attempt to rectify the situation?", + "When you’re working with a large number of customers, it’s tricky to deliver excellent service to them all. How do you go about prioritizing your customers’ needs?", + "Tell me about settling into your last job. What did you do to learn the ropes?", + "Tell me about a time you failed. How did you deal with the situation?", + "Tell me about a time you set a goal for yourself. How did you go about ensuring that you would meet your objective?", + "Describe a long-term project that you kept on track. How did you keep everything moving?", + "Give me an example of a time when you were able to successfully persuade someone at work to see things your way.", + "Tell me about a successful presentation you gave and why you think it was a hit.", + "Describe a time when you saw a problem and took the initiative to correct it.", + "Tell me about a time you were dissatisfied in your role. What could have been done to make it better?"] \ No newline at end of file diff --git a/s3.js b/Tech-Prep-Skill/lambda/s3.js similarity index 100% rename from s3.js rename to Tech-Prep-Skill/lambda/s3.js diff --git a/timerPayload.js b/Tech-Prep-Skill/lambda/timerPayload.js similarity index 100% rename from timerPayload.js rename to Tech-Prep-Skill/lambda/timerPayload.js diff --git a/util.js b/Tech-Prep-Skill/lambda/util.js similarity index 100% rename from util.js rename to Tech-Prep-Skill/lambda/util.js diff --git a/Tech-Prep-Skill/skill.json b/Tech-Prep-Skill/skill.json new file mode 100644 index 0000000..3549bbd --- /dev/null +++ b/Tech-Prep-Skill/skill.json @@ -0,0 +1,56 @@ +{ + "manifest": { + "apis": { + "custom": { + "endpoint": { + "uri": "arn:aws:lambda:us-east-1:884399042415:function:14e99778-a6d9-4635-ba05-223cb8536b8f:Release_0" + }, + "interfaces": [], + "regions": { + "EU": { + "endpoint": { + "uri": "arn:aws:lambda:eu-west-1:884399042415:function:14e99778-a6d9-4635-ba05-223cb8536b8f:Release_0" + } + }, + "NA": { + "endpoint": { + "uri": "arn:aws:lambda:us-east-1:884399042415:function:14e99778-a6d9-4635-ba05-223cb8536b8f:Release_0" + } + }, + "FE": { + "endpoint": { + "uri": "arn:aws:lambda:us-west-2:884399042415:function:14e99778-a6d9-4635-ba05-223cb8536b8f:Release_0" + } + } + } + } + }, + "manifestVersion": "1.0", + "permissions": [ + { + "name": "alexa::profile:email:read" + }, + { + "name": "alexa::alerts:timers:skill:readwrite" + } + ], + "publishingInformation": { + "distributionCountries": [], + "isAvailableWorldwide": true, + "locales": { + "en-US": { + "description": "Sample Full Description", + "examplePhrases": [ + "Alexa open hello world", + "hello", + "help" + ], + "keywords": [], + "name": "TechPrep", + "summary": "Sample Short Description" + } + }, + "testingInstructions": "Sample Testing Instructions." + } + } +} \ No newline at end of file diff --git a/questions.json b/questions.json deleted file mode 100644 index 8f75524..0000000 --- a/questions.json +++ /dev/null @@ -1,13 +0,0 @@ -["Tell me about a time when you had to work closely with someone whose personality was very different from yours.", - "Give me an example of a time you faced a conflict with a coworker. How did you handle that?", - "Describe a time when you had to step up and demonstrate leadership skills.", - "Give me an example of a time when you didn’t meet a client’s expectation. What happened, and how did you attempt to rectify the situation?", - "When you’re working with a large number of customers, it’s tricky to deliver excellent service to them all. How do you go about prioritizing your customers’ needs?", - "Tell me about settling into your last job. What did you do to learn the ropes?", - "Tell me about a time you failed. How did you deal with the situation?", - "Tell me about a time you set a goal for yourself. How did you go about ensuring that you would meet your objective?", - "Describe a long-term project that you kept on track. How did you keep everything moving?", - "Give me an example of a time when you were able to successfully persuade someone at work to see things your way.", - "Tell me about a successful presentation you gave and why you think it was a hit.", - "Describe a time when you saw a problem and took the initiative to correct it.", - "Tell me about a time you were dissatisfied in your role. What could have been done to make it better?"] \ No newline at end of file From 280f8b92176d1749dc74738d53aa460d96b9a664 Mon Sep 17 00:00:00 2001 From: Jack Stubblefield Date: Sat, 22 Oct 2022 14:11:14 -0500 Subject: [PATCH 6/7] removed API key --- Tech-Prep-Skill/lambda/logic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tech-Prep-Skill/lambda/logic.js b/Tech-Prep-Skill/lambda/logic.js index be3359b..b4fb857 100644 --- a/Tech-Prep-Skill/lambda/logic.js +++ b/Tech-Prep-Skill/lambda/logic.js @@ -12,7 +12,7 @@ const options = { num_pages: '1' }, headers: { - 'X-RapidAPI-Key': '06a16c25e6msh7182d6c514e7e7bp1047ddjsne068aa9f6a0e', + 'X-RapidAPI-Key': 'YOUR API KEY', 'X-RapidAPI-Host': 'jsearch.p.rapidapi.com' } }; From e884125d65a66a083e03fd1aae6e85fb432b9c9e Mon Sep 17 00:00:00 2001 From: Jack Stubblefield Date: Sat, 22 Oct 2022 14:12:45 -0500 Subject: [PATCH 7/7] removed Alexa ARNS --- Tech-Prep-Skill/lambda/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tech-Prep-Skill/lambda/index.js b/Tech-Prep-Skill/lambda/index.js index 881d381..0f73372 100644 --- a/Tech-Prep-Skill/lambda/index.js +++ b/Tech-Prep-Skill/lambda/index.js @@ -90,7 +90,7 @@ const FetchJobsIntentHandler = { const credentials = await sts .assumeRole( { - RoleArn: 'arn:aws:iam::948331367146:role/AlexaX', + RoleArn: 'YOUR ARN', RoleSessionName: 'SendEmailRoleSession' }, (err, res) => { @@ -247,7 +247,7 @@ const EmailIntentHandler = { const credentials = await sts .assumeRole( { - RoleArn: 'arn:aws:iam::948331367146:role/Alexa', + RoleArn: 'YOUR ARN', RoleSessionName: 'SendEmailRoleSession' }, (err, res) => { @@ -490,7 +490,7 @@ const GetChallengeIntentHandler = { const credentials = await sts .assumeRole( { - RoleArn: 'arn:aws:iam::143451010179:role/Alexa', + RoleArn: 'YOUR ARN', RoleSessionName: 'SendEmailRoleSession' }, (err, res) => {