├── src ├── components │ ├── index.js │ └── NewsCards │ │ ├── styles.js │ │ ├── NewsCard │ │ ├── styles.js │ │ └── NewsCard.js │ │ └── NewsCards.js ├── index.js ├── index.css ├── styles.js └── App.js ├── public ├── manifest.json └── index.html ├── README.md ├── package.json └── Alan Studio Code scripts ├── Calendar.js ├── Yoda.js ├── News.js ├── Directions.js ├── news_reader_app.js ├── Calculator.js ├── Weather.js ├── SmallTalk.js └── Coffee.js /src/components/index.js: -------------------------------------------------------------------------------- 1 | 2 | export { default as NewsCards } from './NewsCards/NewsCards'; 3 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import './index.css'; 5 | 6 | import App from './App'; 7 | 8 | ReactDOM.render(, document.getElementById('root')); 9 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Aiden AI News", 3 | "name": "Aiden AI News App", 4 | "icons": [ 5 | { 6 | "src": "https://is5-ssl.mzstatic.com/image/thumb/Purple115/v4/80/48/e3/8048e37d-0d91-b1c4-b52f-9a9f2a5b2519/source/512x512bb.jpg", 7 | "sizes": "512x512", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@500&display=swap'); 2 | 3 | #root, html, body { 4 | height: 100vh; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | background-color: #ecf0f1; 10 | background-image: 'https://serving.photos.photobox.com/22945454270eddba6d471f4eaffb6686b2c2006eaf67d7179fda7e2379d31f03a7b3f640.jpg'; 11 | } 12 | 13 | * { 14 | box-sizing: border-box; 15 | font-family: 'Roboto Mono', monospace; 16 | } 17 | 18 | html { 19 | scroll-behavior: smooth; 20 | } -------------------------------------------------------------------------------- /src/components/NewsCards/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@material-ui/core/styles'; 2 | 3 | export default makeStyles({ 4 | card: { 5 | display: 'flex', 6 | flexDirection: 'column', 7 | justifyContent: 'space-between', 8 | alignItems: 'center', 9 | width: '100%', 10 | height: '45vh', 11 | padding: '10%', 12 | borderRadius: 10, 13 | color: '#f7f1e3', 14 | }, 15 | infoCard: { 16 | display: 'flex', flexDirection: 'column', textAlign: 'center', 17 | }, 18 | container: { 19 | padding: '0 5%', width: '100%', margin: 0, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Aiden: AI News App 13 | 14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/NewsCards/NewsCard/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@material-ui/core/styles'; 2 | 3 | export default makeStyles({ 4 | media: { 5 | height: 250, 6 | }, 7 | border: { 8 | border: 'solid', 9 | }, 10 | fullHeightCard: { 11 | height: '100%', 12 | }, 13 | card: { 14 | display: 'flex', 15 | flexDirection: 'column', 16 | justifyContent: 'space-between', 17 | borderBottom: '10px solid white', 18 | }, 19 | activeCard: { 20 | borderBottom: '10px solid #22289a', 21 | }, 22 | grid: { 23 | display: 'flex', 24 | }, 25 | details: { 26 | display: 'flex', 27 | justifyContent: 'space-between', 28 | margin: '20px', 29 | }, 30 | title: { 31 | padding: '0 16px', 32 | }, 33 | cardActions: { 34 | padding: '0 16px 8px 16px', 35 | display: 'flex', 36 | justifyContent: 'space-between', 37 | }, 38 | }); 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aiden: AI News App 🗞️👾 2 | ### AI-powered with voice recognition enabled app that reads news from a variety of sources, interacts with you, and can even have funny and relevant conversations. 3 | 4 | ## 🤙 [See Aiden live!](https://aidenapp.netlify.app//) 🤙 5 | 6 | ## How To Run: 7 | 8 | ### npm i && npm start - this will start the development server after installing the node modules 9 | 10 | 11 | 12 | ## 🧤 Packages/Libraries/Stuff that I used with React: 13 | 14 | * ### npm i @material-ui/core 15 | * ### npm i alan-ai/alan-sdk-web 16 | * ### npm i words-to-numbers 17 | 18 | 19 | ## I coded the AI scripts on [Alan Studio Code](https://alan.app/) 🤖 20 | ### *I also included the AI scripts folder in this repository* 21 | 22 | ## ✔ [API I used](https://newsapi.org/) 23 | 24 | #### N.B. : Do not turn off your pop-ups in your browser or Aiden won't be able to read the articles 25 | --- 26 | ![news with aiden (4)](https://user-images.githubusercontent.com/55017730/92018793-3f76e580-ed73-11ea-9a9e-4b9634002836.png) 27 | --- 28 | ![screenshot](https://user-images.githubusercontent.com/55017730/92019152-ca57e000-ed73-11ea-99d5-a37ba7587acb.png) 29 | --- 30 | 31 | ![Talk to Aiden](https://user-images.githubusercontent.com/55017730/92018891-69c8a300-ed73-11ea-9c02-068b45128216.png) 32 | --- 33 | ![aiden times](https://user-images.githubusercontent.com/55017730/92018833-50275b80-ed73-11ea-9643-2cae957b3e2c.png) 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "http://proghead00.github.io/Aiden-AI-News-App", 3 | "name": "alan_react", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@alan-ai/alan-sdk-web": "^1.1.8", 8 | "@material-ui/core": "^4.10.1", 9 | "@material-ui/lab": "^4.0.0-alpha.56", 10 | "@testing-library/jest-dom": "^4.2.4", 11 | "@testing-library/react": "^9.5.0", 12 | "@testing-library/user-event": "^7.2.1", 13 | "axios": "^0.19.2", 14 | "classnames": "^2.2.6", 15 | "emailjs-com": "^2.6.3", 16 | "react": "^16.13.1", 17 | "react-dom": "^16.13.1", 18 | "react-scripts": "^3.4.3", 19 | "react-spinners": "^0.8.3", 20 | "words-to-numbers": "^1.5.1" 21 | }, 22 | "scripts": { 23 | "predeploy" :"npm run build", 24 | "deploy" : "gh-pages -d build", 25 | "start": "react-scripts start", 26 | "build": "react-scripts build", 27 | "test": "react-scripts test", 28 | "eject": "react-scripts eject" 29 | }, 30 | "eslintConfig": { 31 | "extends": "react-app" 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | }, 45 | "devDependencies": { 46 | "eslint": "^6.8.0", 47 | "eslint-config-airbnb": "^18.1.0", 48 | "eslint-plugin-import": "^2.21.2", 49 | "eslint-plugin-jsx-a11y": "^6.2.3", 50 | "eslint-plugin-react": "^7.20.0", 51 | "eslint-plugin-react-hooks": "^2.5.1", 52 | "gh-pages": "^3.1.0" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/styles.js: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@material-ui/core/styles'; 2 | 3 | export default makeStyles((theme) => ({ 4 | footer: { 5 | textAlign: 'center', 6 | position: 'relative', 7 | left: 0, 8 | bottom: 0, 9 | color: 'black', 10 | width: '100%', 11 | display: 'flex', 12 | alignItems: 'center', 13 | justifyContent: 'center', 14 | height: '120px', 15 | [theme.breakpoints.down('sm')]: { 16 | display: 'none', 17 | }, 18 | }, 19 | link: { 20 | textDecoration: 'none', 21 | color: '#2c3e50', 22 | marginRight : 10, 23 | 24 | }, 25 | 26 | 27 | 28 | image: { 29 | marginLeft: 20, 30 | }, 31 | card: { 32 | display: 'flex', 33 | justifyContent: 'center', 34 | alignItems: 'center', 35 | width: '50%', 36 | padding: '3%', 37 | borderRadius: 10, 38 | color: 'white', 39 | backgroundColor: 'rgba(207, 106, 135,1.0)', 40 | margin: '0 12px', 41 | textAlign: 'center', 42 | height: '25vmin', 43 | [theme.breakpoints.down('sm')]: { 44 | flexDirection: 'column-reverse', 45 | textAlign: 'center', 46 | width: '100%', 47 | height: 'initial', 48 | '&:nth-of-type(1)': { 49 | marginBottom: '12px', 50 | }, 51 | }, 52 | }, 53 | infoContainer: { 54 | display: 'flex', 55 | alignItems: 'center', 56 | justifyContent: 'space-around', 57 | [theme.breakpoints.down('sm')]: { 58 | flexDirection: 'column', 59 | }, 60 | }, 61 | logoContainer: { 62 | padding: '0 5%', 63 | display: 'flex', 64 | justifyContent: 'space-around', 65 | alignItems: 'center', 66 | width: '100%', 67 | [theme.breakpoints.down('sm')]: { 68 | flexDirection: 'column-reverse', 69 | textAlign: 'center', 70 | }, 71 | }, 72 | alanLogo: { 73 | height: '60vmin', 74 | borderRadius: '15%', 75 | padding: '0 5%', 76 | margin: '3% 0', 77 | [theme.breakpoints.down('sm')]: { 78 | height: '35vmin', 79 | }, 80 | }, 81 | })); 82 | -------------------------------------------------------------------------------- /src/components/NewsCards/NewsCards.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Grid, Grow, Typography } from '@material-ui/core'; 3 | 4 | import NewsCard from './NewsCard/NewsCard'; 5 | import useStyles from './styles.js'; 6 | 7 | const infoCards = [ 8 | 9 | { color: '#079992', title: 'Latest News', text: 'Give me the latest news' }, 10 | { color: '#eb3b5a', title: 'News by Categories', info: 'Business, Entertainment, General, Health, Science, Sports, Technology', text: 'Give me the latest technology news' }, 11 | { color: '#2C3A47', title: 'News by Terms', info: 'Bitcoin, PlayStation 5, Smartphones...', text: 'What\'s up with Bitcoin' }, 12 | { color: '#FD7272', title: 'News by Sources', info: 'CNN, Wired, BBC News, Time, IGN, Buzzfeed...', text: 'Give me the news from CNN' }, 13 | 14 | 15 | 16 | ]; 17 | 18 | const NewsCards = ({ articles, activeArticle }) => { 19 | const classes = useStyles(); 20 | 21 | if (!articles.length) { 22 | return ( 23 | 24 | 25 | {infoCards.map((infoCard) => ( 26 | 27 |
28 | {infoCard.title} 29 | {infoCard.info ? {infoCard.title.split(' ')[2]}:
{infoCard.info}
: null} 30 | Try saying:
{infoCard.text}
31 |
32 |
33 | ))} 34 |
35 |
36 | ); 37 | } 38 | 39 | return ( 40 | 41 | 42 | {articles.map((article, i) => ( 43 | 44 | 45 | 46 | ))} 47 | 48 | 49 | ); 50 | }; 51 | 52 | export default NewsCards; 53 | -------------------------------------------------------------------------------- /src/components/NewsCards/NewsCard/NewsCard.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, createRef } from 'react'; 2 | import { Card, CardActions, CardActionArea, CardContent, CardMedia, Button, Typography } from '@material-ui/core'; 3 | import classNames from 'classnames'; 4 | 5 | import useStyles from './styles'; 6 | 7 | const NewsCard = ({ article: { description, publishedAt, source, title, url, urlToImage }, activeArticle, i }) => { 8 | const classes = useStyles(); 9 | const [ elRefs, setElRefs ] = useState([]); 10 | const scrollToRef = (ref) => window.scroll(0, ref.current.offsetTop - 50); 11 | 12 | useEffect(() => { 13 | window.scroll(0, 0); 14 | 15 | setElRefs((refs) => Array(20).fill().map((_, j) => refs[j] || createRef())); 16 | }, []); 17 | 18 | useEffect( 19 | () => { 20 | if (i === activeArticle && elRefs[activeArticle]) { 21 | scrollToRef(elRefs[activeArticle]); 22 | } 23 | }, 24 | [ i, activeArticle, elRefs ] 25 | ); 26 | 27 | return ( 28 | 29 | 30 | 38 |
39 | 40 | {new Date(publishedAt).toDateString()} 41 | 42 | 43 | {source.name} 44 | 45 |
46 | 47 | {title} 48 | 49 | 50 | 51 | {description} 52 | 53 | 54 |
55 | 56 | 59 | 60 | {i + 1} 61 | 62 | 63 |
64 | ); 65 | }; 66 | 67 | export default NewsCard; 68 | -------------------------------------------------------------------------------- /Alan Studio Code scripts/Calendar.js: -------------------------------------------------------------------------------- 1 | // {Name: Calendar} 2 | // {Description: What day is tomorrow} 3 | 4 | title("General calendar") 5 | 6 | intent("what (date|day) $(V is|was|will be|would be|) $(DATE) $(T next year|last year|)", 7 | p => { 8 | if (p.T.value === "last year"){ 9 | p.DATE = p.DATE.moment.add(-1, 'Y'); 10 | } 11 | else if (p.T.value === "next year"){ 12 | p.DATE = p.DATE.moment.add(1, 'Y'); 13 | } 14 | let res = p.DATE.moment.format("dddd, MMMM Do YYYY"); 15 | p.play(`${p.DATE} ${p.V} ` + res) 16 | }) 17 | 18 | follow("(and|) (what|) (about|) $(DATE)", 19 | p => { 20 | let res = p.DATE.moment.format("dddd, MMMM Do YYYY"); 21 | p.play(`${p.DATE} ` + res) 22 | }) 23 | 24 | intent("(what is|) is (my|) timezone", 25 | p => { 26 | p.play("Your current timezone is " + p.timeZone); 27 | }) 28 | 29 | intent("(what is|) (the|) (current|) time (now|)", 30 | p => { 31 | p.play("Now is " + api.moment().tz(p.timeZone).format("h:mmA")); 32 | }) 33 | 34 | intent("(what is|) (the|) (current|) (day|date) (now|today|)", 35 | p => { 36 | p.play("Now is " + api.moment().tz(p.timeZone).format("dddd, MMMM Do YYYY")); 37 | }) 38 | 39 | intent("(what is|) (the|) (current|) day and time (now|today|)", 40 | p => { 41 | p.play("Now is " + api.moment().tz(p.timeZone).format("dddd, h:mmA")); 42 | }) 43 | 44 | 45 | title("Alan calendar") 46 | 47 | intent("when (Alan|) Turing was born", 48 | p => { 49 | let turingBirthdate = api.moment("19120612", "YYYYMMDD"); 50 | p.play(`Alan Turing was born 51 | ${turingBirthdate.fromNow()} 52 | on ${turingBirthdate.format("dddd, MMMM Do YYYY")}`) 53 | }) 54 | 55 | 56 | title("Moon landing calendar") 57 | 58 | intent("when was the first (unmanned|) (moon landing|lunar landing)", 59 | p => { 60 | let moonLandingDateLuna = api.moment("19590913", "YYYYMMDD"); 61 | p.play(`The first unmanned moon landing was on 62 | ${moonLandingDateLuna.format("dddd, MMMM Do YYYY")}, 63 | ${moonLandingDateLuna.fromNow()}`) 64 | }) 65 | 66 | var mannedLanding = p => { 67 | let moonLandingDateApollo = api.moment("19690720", "YYYYMMDD"); 68 | p.play(`The first manned moon landing was on 69 | ${moonLandingDateApollo.format("dddd, MMMM Do YYYY")}, 70 | ${moonLandingDateApollo.fromNow()}`) 71 | } 72 | 73 | follow("and manned", mannedLanding) 74 | 75 | intent("when was the first manned (moon landing|lunar landing)", mannedLanding) 76 | 77 | // see https://momentjs.com, moment js library is available through api.moment -------------------------------------------------------------------------------- /Alan Studio Code scripts/Yoda.js: -------------------------------------------------------------------------------- 1 | // {Name: Yoda} 2 | // {Description: Convert user text into the way how Yoda would say it} 3 | 4 | project.statements = [ 5 | "Train yourself to let go of everything you fear to lose", 6 | "Fear is the path to the dark side. Fear leads to anger. Anger leads to hate. Hate leads to suffering.", 7 | "Death is a natural part of life. Rejoice for those around you who transform into the Force. Mourn them do not. Miss them do not. Attachment leads to jealousy. The shadow of greed, that is.", 8 | "Always pass on what you have learned.", 9 | "Once you start down the dark path, forever will it dominate your destiny, consume you it will.", 10 | "In a dark place we find ourselves, and a little more knowledge lights our way.", 11 | "When you look at the dark side, careful you must be. For the dark side looks back.", 12 | "Many of the truths that we cling to depend on our point of view.", 13 | "Do or do not. There is no try.", 14 | "You will find only what you bring in.", 15 | "Always two there are, no more, no less. A master and an apprentice.", 16 | "The dark side clouds everything. Impossible to see the future is.", 17 | "You must unlearn what you have learned.", 18 | "Named must be your fear before banish it you can.", 19 | "Fear is the path to the dark side. Fear leads to anger. Anger leads to hate. Hate leads to suffering.", 20 | "The greatest teacher, failure is.", 21 | "Difficult to see. Always in motion is the future.", 22 | ] 23 | 24 | const yodaVoice = voice('de'); 25 | 26 | const whatPhrase = context(()=> { 27 | follow(`(How|) (do|would) (you|) (say|translate) $(I* (.+))`, p=> { 28 | p.resolve(p.I.value); 29 | }); 30 | follow(`Stop this madness`, p => { 31 | p.play('ok sir!!') 32 | }); 33 | }); 34 | intent("(Give me some|) (inspiration|wisdom|)", "What would Yoda (say|tell me|believe|)?", 35 | "(Yoda|) (Enlighten|Inspire|) (me|)", p => { 36 | var temp = Math.floor(Math.random(10) * project.statements.length) 37 | p.play(project.statements[temp]) 38 | }); 39 | intent("testing the widget", p =>{ 40 | p.play({embeddedPage:true, page: "yoda.html"}) 41 | }) 42 | intent(`(Show me|) what can I do here?`, `How does this app work?`, p => { 43 | p.play(`In this application, you can ask Yoda for some wisdom or to translate text.`); 44 | }); 45 | function apiCall(p, command, param, callback) { 46 | let jsp = { 47 | url: "https://studio.alan.app/api_playground/" + command, 48 | strictSSL: false, 49 | method: 'POST', 50 | json: param, 51 | timeout: 3000, 52 | }; 53 | api.request(jsp, (err, res, body)=> { 54 | if (err || res.statusCode !== 200) { 55 | p.play('(Sorry|) something went wrong (on the server|)'); 56 | } else if (body.error) { 57 | p.play(body.error); 58 | } else { 59 | callback(body); 60 | } 61 | }); 62 | } 63 | intent(`(How|) (do|would) (you|) (say|translate) $(I* (.*))`, async p => { 64 | let phrase = p.I.value; 65 | if (!phrase.length) { 66 | p.play('should I say what?'); 67 | phrase = await p.then(whatPhrase); 68 | } 69 | apiCall(p, 'askYoda', {query: phrase}, response => { 70 | if(!response.error) { 71 | p.play(yodaVoice, response.translated); 72 | } else { 73 | console.log(response.error); 74 | } 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /Alan Studio Code scripts/News.js: -------------------------------------------------------------------------------- 1 | // {Name: News} 2 | // {Description: Gives the latest headlines on topics like health, science, entertainment, sports, business, and technology. Each news headline has a corresponding image. } 3 | 4 | title("News") 5 | 6 | const page = 5; 7 | const key = "7bdfb1b10aca41c6becea47611b7c35a"; 8 | 9 | let TOPICS = ["business", "entertainment", "general", "health", "science", "sports", "technology"]; 10 | let TOPICS_INTENT = []; 11 | for (let i = 0; i < TOPICS.length; i++ ) { 12 | TOPICS_INTENT.push(TOPICS[i] + "~" + TOPICS[i]); 13 | } 14 | TOPICS_INTENT = TOPICS_INTENT.join('|') + '|'; 15 | 16 | intent(`(show|what is|tell me|what's|what are|what're|read) (the|) (recent|latest|) $(N news|headlines) (in|about|on|) $(T~ ${TOPICS_INTENT})`, 17 | `(read|show|get|bring me) (the|) (recent|latest|) $(T~ ${TOPICS_INTENT}) $(N news|headlines)`, 18 | p => { 19 | let headlinesUrl = "https://newsapi.org/v2/top-headlines?country=us&apiKey=7bdfb1b10aca41c6becea47611b7c35a"; 20 | if (p.T.label) { 21 | headlinesUrl = headlinesUrl + "&category=" + p.T.label; 22 | } 23 | api.request(headlinesUrl, function (error, response, body) { 24 | if (error || response && response.statusCode !== 200) { 25 | print('failed to get news' + error); 26 | p.play(`(Sorry,|) (Something went wrong.|There was an error.|) (I'm unable to get the news at this time.|I wasn't able to get the news.) (Please try again.|)`); 27 | } else { 28 | let headlines = []; 29 | let images = []; 30 | let res = JSON.parse(body); 31 | let articles = res.articles; 32 | let max = Math.min(page, articles.length); 33 | for (let i = 0; i < max; i++ ) { 34 | let article = articles[i]; 35 | let name = article.source.name; 36 | let author = article.author; 37 | let title = article.title; 38 | let description = article.description; 39 | let image = article.urlToImage; 40 | if (title) { 41 | headlines.push(title); 42 | images.push(image); 43 | } 44 | } 45 | p.play({embeddedPage:true, page: "news.html", command: "newHeadlines", headlines: headlines, images: images}); 46 | if (p.T && p.T.label) { 47 | p.play(`Here are the (latest|recent) $(N headlines) on ${p.T.label}.`, 48 | `Here's the (recent|latest) $(N news) on ${p.T.label}.`, 49 | `Here are the (latest|recent) ${p.T.label} $(N headlines).`, 50 | `Here's the (recent|latest) ${p.T.label} $(N news)`); 51 | } else { 52 | p.play(`Here are the (latest|recent) $(N headlines).`, 53 | `Here's the (latest|recent) $(N news).`); 54 | } 55 | for (let y = 0; y < headlines.length; y++ ) { 56 | p.play({embeddedPage:true, command: "highlight", page: "news.html", head: headlines[y], image: images[y]}); 57 | p.play(`${headlines[y]}`); 58 | } 59 | p.play({embeddedPage:true, command: "unSelect"}); 60 | } 61 | }) 62 | }) 63 | 64 | intent(`What does this app do?`, `How does this work?`, `What can I do here?`, `How should I use this?`, 65 | reply(`This is a news project, and you can provide the most recent headlines in mainstream media` + 66 | ` Just ask me anything about the news, and I will try to answer it`)); 67 | 68 | intent(`(types|categories|topics) (of|in) the news (now|)`, `What (types|categories|topics) of news do you have?`, 69 | reply(`We provide news on ` + TOPICS.join(", "))); 70 | -------------------------------------------------------------------------------- /Alan Studio Code scripts/Directions.js: -------------------------------------------------------------------------------- 1 | // {Name: Directions} 2 | // {Description: Provides navigation and location information. "Take me to my next meeting", "Where is my meeting tomorrow morning?"} 3 | 4 | title("Change address.") 5 | 6 | intent("(change|update|set) $(L home|office|work) (address|location) to $(LOC)", 7 | reply("do you want to set $(L) address to $(LOC)", 8 | follow("(yes|ok|sure)", reply("ok, set")), 9 | follow("(no|nope|cancel)", reply("cancelled")))) 10 | 11 | intent("(remember|set|update) (this|current|that) (location|address|) as $(L home|office|work) (address|)", 12 | p => { 13 | p.play("set") 14 | }) 15 | 16 | 17 | let where = context(()=> { 18 | title("Question where?") 19 | 20 | follow("(to my|to|my|) $(L home|office|work|gym)", 21 | async p => p.resolve(await getLocation(p, "your", p.L.value))) 22 | 23 | follow("$(NAME) ('s|) $(L home|office|work)", 24 | async p => p.resolve(await getLocation(p, p.NAME.value, p.L.value))) 25 | 26 | follow("(to|) $(C next|last|current) $(E meeting|event|appointment)", 27 | p => p.play(`navigating to ${p.C} ${p.E}`)) 28 | 29 | follow("$(LOC)", 30 | p => p.resolve(p.LOC.value)) 31 | 32 | follow('never mind', '(cancel|forget) (that|)', 33 | p => p.resolve(null)) 34 | 35 | fallback( 36 | 'Where should I navigate? It can be home, (office|work), work or a specific address', 37 | 'I can navigate to home, office, (work|gym) or a specific address') 38 | }) 39 | 40 | let whatAddress = context(() => { 41 | title("Question what address?") 42 | 43 | onCreateContext(p => { 44 | p.state = [] 45 | }) 46 | 47 | follow("$(NUMBER)", 48 | p => { 49 | p.state.push(p.NUMBER.number) 50 | checkAddress(p) 51 | }) 52 | 53 | follow("(it's|the address is|) $(LOC)", 54 | p => { 55 | p.state.push(p.LOC.value) 56 | checkAddress(p) 57 | }) 58 | 59 | follow('never mind', '(cancel|forget) (that|)', 60 | p => { 61 | p.resolve(null) 62 | }) 63 | 64 | fallback('(Please|Just|) tell me the (address|location)', 65 | 'I need the (location|address)') 66 | 67 | function checkAddress(p) { 68 | let a = parseAddress(p.state) 69 | if (a.valid) { 70 | p.resolve(a.address) 71 | } else { 72 | //p.onIdle(3000, function() { 73 | p.play("please tell me a valid address") 74 | //}) 75 | } 76 | } 77 | }) 78 | 79 | function parseAddress(items) { 80 | let addr = items.join(' '); 81 | if (addr.length > 5) { 82 | return {valid: true, address: addr}; 83 | } 84 | return {valid: false}; 85 | } 86 | 87 | function getLocation(p, person, loc) { 88 | let address = null;//''api.getAddress(person, loc) 89 | if (address) { 90 | return Promise.resolve(address); 91 | } else { 92 | p.play(`what is ${person} ${loc} address?`); 93 | return p.then(whatAddress); 94 | } 95 | } 96 | 97 | 98 | title("Navigation.") 99 | 100 | intent("navigate (me|)", "build (me|) (a|) route (for me|)", 101 | reply("where (|to)?", 102 | async p => { 103 | let address = await p.then(where) 104 | if (address) { 105 | p.play("navigating to " + address) 106 | } else { 107 | p.play('(ok|fine|sure)') 108 | } 109 | })) 110 | 111 | intent("(navigate me|build route|get me) (to|) $(LOC)", 112 | p => { 113 | p.play(`navigating to ${p.LOC}`) 114 | }) 115 | 116 | intent("(navigate|take me|get me|build route) (to|) (my|) $(L home|office|work|gym)", 117 | "(get|take) me (to|) (my|) $(L home)", 118 | async p => { 119 | let addr = await getLocation(p, "your", p.L.value) 120 | if (addr) { 121 | p.play(`navigating to ${p.L}, ${addr}`) 122 | } else { 123 | p.play('(ok|fine|sure)') 124 | } 125 | }) 126 | 127 | 128 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Typography } from '@material-ui/core'; 3 | import wordsToNumbers from 'words-to-numbers'; 4 | import alanBtn from '@alan-ai/alan-sdk-web' ; 5 | 6 | 7 | import NewsCards from './components/NewsCards/NewsCards'; 8 | import useStyles from './styles'; 9 | 10 | const App = () => { 11 | const [ activeArticle, setActiveArticle ] = useState(0); 12 | const [ newsArticles, setNewsArticles ] = useState([]); 13 | 14 | 15 | const classes = useStyles(); 16 | 17 | useEffect(() => { 18 | alanBtn({ 19 | key: ' ', 20 | onCommand: ({ command, articles, number }) => { 21 | if (command === 'newHeadlines') { 22 | setNewsArticles(articles); 23 | setActiveArticle(-1); 24 | } 25 | 26 | else if (command === 'highlight') { 27 | setActiveArticle((prevActiveArticle) => prevActiveArticle + 1); 28 | } else if (command === 'open') { 29 | const parsedNumber = number.length > 2 ? wordsToNumbers(number, { fuzzy: true }) : number; 30 | const article = articles[parsedNumber - 1]; 31 | 32 | if (parsedNumber > 20) { 33 | alanBtn().playText('Please try that again...'); 34 | } else if (article) { 35 | window.open(article.url, '_blank'); 36 | alanBtn().playText('Opening...'); 37 | } else { 38 | alanBtn().playText('Please try that again...'); 39 | } 40 | } 41 | } 42 | }); 43 | }, []); 44 | 45 | return ( 46 |
47 |
48 | {newsArticles.length ? ( 49 |
50 | 51 |
52 | 53 | Try saying:
54 |
Open article number 4 55 |
56 |
57 |
58 | 59 | Try saying:
60 |
Go back 61 |
62 |
63 |
64 | ) : null} 65 | 66 | 67 | aiden logo 68 |
69 | 70 | 71 | {!newsArticles.length ? ( 72 |
73 | 74 | Built by 75 | 76 | 77 | 78 | {' '} 79 | Susnata Goswami 80 |
81 | 82 | 83 |
84 |
85 | Click 86 | 87 | {' '} 88 | here 89 | 90 | to see what else you can do with Aiden! 91 |
92 | 93 | 94 | 95 | 96 | N.B: Please don't turn off pop-ups on your browser or Aiden won't be able to read the news 97 | 98 | {/* 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | */} 112 | 113 | 114 | 115 | 116 | 117 | {' '} 118 | 119 |
120 | 121 |
122 | ) : null} 123 |
124 | ); 125 | }; 126 | 127 | export default App; 128 | -------------------------------------------------------------------------------- /Alan Studio Code scripts/news_reader_app.js: -------------------------------------------------------------------------------- 1 | intent('What does this app do?', 'What can I do here?', 2 | reply('This is a news project.')); 3 | 4 | const API_KEY = '7bdfb1b10aca41c6becea47611b7c35a'; 5 | let savedArticles = []; 6 | 7 | // News by Source 8 | intent('Give me the news from $(source* (.*))', (p) => { 9 | let NEWS_API_URL = `https://newsapi.org/v2/top-headlines?apiKey=${API_KEY}`; 10 | 11 | if(p.source.value) { 12 | NEWS_API_URL = `${NEWS_API_URL}&sources=${p.source.value.toLowerCase().split(" ").join('-')}` 13 | } 14 | 15 | api.request(NEWS_API_URL, (error, response, body) => { 16 | const { articles } = JSON.parse(body); 17 | 18 | if(!articles.length) { 19 | p.play('Sorry, please try searching for news from a different source'); 20 | return; 21 | } 22 | 23 | savedArticles = articles; 24 | 25 | p.play({ command: 'newHeadlines', articles }); 26 | p.play(`Here are the (latest|recent) ${p.source.value}.`); 27 | 28 | p.play('Would you like me to read the headlines?'); 29 | p.then(confirmation); 30 | }); 31 | }) 32 | 33 | // News by Term 34 | intent('what\'s up with $(term* (.*))', (p) => { 35 | let NEWS_API_URL = `https://newsapi.org/v2/everything?apiKey=${API_KEY}`; 36 | 37 | if(p.term.value) { 38 | NEWS_API_URL = `${NEWS_API_URL}&q=${p.term.value}` 39 | } 40 | 41 | api.request(NEWS_API_URL, (error, response, body) => { 42 | const { articles } = JSON.parse(body); 43 | 44 | if(!articles.length) { 45 | p.play('Sorry, please try searching for something else.'); 46 | return; 47 | } 48 | 49 | savedArticles = articles; 50 | 51 | p.play({ command: 'newHeadlines', articles }); 52 | p.play(`Here are the (latest|recent) articles on ${p.term.value}.`); 53 | 54 | p.play('Would you like me to read the headlines?'); 55 | p.then(confirmation); 56 | }); 57 | }) 58 | 59 | // News by Categories 60 | const CATEGORIES = ['business', 'entertainment', 'general', 'health', 'science', 'sports', 'technology']; 61 | const CATEGORIES_INTENT = `${CATEGORIES.map((category) => `${category}~${category}`).join('|')}|`; 62 | 63 | intent(`(show|what is|tell me|what's|what are|what're|read) (the|) (recent|latest|) $(N news|headlines) (in|about|on|) $(C~ ${CATEGORIES_INTENT})`, 64 | `(read|show|get|bring me|give me) (the|) (recent|latest) $(C~ ${CATEGORIES_INTENT}) $(N news|headlines)`, (p) => { 65 | let NEWS_API_URL = `https://newsapi.org/v2/top-headlines?apiKey=${API_KEY}&country=us`; 66 | 67 | if(p.C.value) { 68 | NEWS_API_URL = `${NEWS_API_URL}&category=${p.C.value}` 69 | } 70 | 71 | api.request(NEWS_API_URL, (error, response, body) => { 72 | const { articles } = JSON.parse(body); 73 | 74 | if(!articles.length) { 75 | p.play('Sorry, please try searching for a different category.'); 76 | return; 77 | } 78 | 79 | savedArticles = articles; 80 | 81 | p.play({ command: 'newHeadlines', articles }); 82 | 83 | if(p.C.value) { 84 | p.play(`Here are the (latest|recent) articles on ${p.C.value}.`); 85 | } else { 86 | p.play(`Here are the (latest|recent) news`); 87 | } 88 | 89 | p.play('Would you like me to read the headlines?'); 90 | p.then(confirmation); 91 | }); 92 | }); 93 | 94 | const confirmation = context(() => { 95 | intent('yes', async (p) => { 96 | for(let i = 0; i < savedArticles.length; i++){ 97 | p.play({ command: 'highlight', article: savedArticles[i]}); 98 | p.play(`${savedArticles[i].title}`); 99 | } 100 | }) 101 | 102 | intent('no', (p) => { 103 | p.play('Sure, not a problem.') 104 | }) 105 | }) 106 | 107 | intent('open (the|) (article|) (number|) $(number* (.*))', (p) => { 108 | if(p.number.value) { 109 | p.play({ command:'open', number: p.number.value, articles: savedArticles}) 110 | } 111 | }) 112 | 113 | intent('(go|) back', (p) => { 114 | p.play('Okay, going back'); 115 | p.play({ command: 'newHeadlines', articles: []}) 116 | }) -------------------------------------------------------------------------------- /Alan Studio Code scripts/Calculator.js: -------------------------------------------------------------------------------- 1 | // {Name: Calculator} 2 | // {Description: Provides responses for basic math queries.} 3 | 4 | 5 | title('Calculator'); 6 | 7 | function plus(v1, v2) { 8 | return v1 + v2; 9 | } 10 | 11 | function minus(v1, v2) { 12 | return v1 - v2; 13 | } 14 | 15 | function mult(v1, v2) { 16 | return v1 * v2; 17 | } 18 | 19 | function divide(v1, v2) { 20 | if (v2 === 0) { 21 | return 'infinity'; 22 | } 23 | return v1 / v2; 24 | } 25 | 26 | function squareRoot(v1) { 27 | if (v1 < 0) { 28 | return 'you can\'t take a square root of a negative number'; 29 | } 30 | return Math.sqrt(v1); 31 | } 32 | 33 | function cubicRoot(v1) { 34 | if (v1 < 0) { 35 | return 'you can\'t take a cubic root of a negative number'; 36 | } 37 | return Math.cbrt(v1); 38 | } 39 | 40 | function roundToLimit(num) { 41 | if(Math.abs(num) >= 1e19 || !num.toString().includes('e')) { 42 | return num; 43 | } 44 | const digitsCount = 15 - numDigits(num); 45 | return +(Math.round(num + 'e+' + digitsCount) + 'e-' + digitsCount); 46 | } 47 | 48 | function numDigits(x) { 49 | return Math.max(Math.floor(Math.log10(Math.abs(x))), 0) + 1; 50 | } 51 | 52 | function power(x, n) { 53 | return Math.pow(x, n); 54 | } 55 | 56 | function cube(x) { 57 | return power(x,3); 58 | } 59 | 60 | function square(x) { 61 | return power(x,2); 62 | } 63 | 64 | const operatorMap = { 65 | 'plus': plus, 66 | 'add': plus, 67 | '+': plus, 68 | 'minus': minus, 69 | 'subtract': minus, 70 | 'take away': minus, 71 | '-': minus, 72 | 'times': mult, 73 | 'multiply': mult, 74 | '*': mult, 75 | 'cubed': cube, 76 | 'squared': square, 77 | 'to the power': power, 78 | 'power': power, 79 | 'divide': divide, 80 | 'divided': divide, 81 | 'over': divide, 82 | '/': divide, 83 | 'cubic root': cubicRoot, 84 | 'square root': squareRoot, 85 | 'root': squareRoot, 86 | }; 87 | 88 | onCreateContext(p => { 89 | p.state.result = 0; 90 | }); 91 | 92 | intent( 93 | '(what is|how much is|calculate|compute|) $(NUMBER) $(OPERATOR *|+|-|/|plus|minus|over|divided|divide|times|to the power) (of|) $(NUMBER)', 94 | '(what is|how much is|calculate|compute|) $(OPERATOR multiply|divide) $(NUMBER) (by|on|) $(NUMBER)', 95 | '(what is|how much is|calculate|compute|) $(OPERATOR cubic root|square root|root) of $(NUMBER)', 96 | '(what is|how much is|calculate|compute|) $(NUMBER) $(OPERATOR cubed|squared)', 97 | '(what is|how much is|calculate|compute|) $(NUMBER) to the $(ORDINAL) $(OPERATOR power)', 98 | p => { 99 | const operator = operatorMap[p.OPERATOR.value]; 100 | 101 | if (!operator) { 102 | p.play(`(Sorry|) I can't do ${p.OPERATOR} (yet|)`); 103 | return; 104 | } 105 | 106 | if (!p.NUMBER_.length) { 107 | p.play('I need at least one argument'); 108 | return; 109 | } 110 | 111 | if (p.NUMBER_.length === 1) { 112 | if(p.ORDINAL) { 113 | p.state.result = operator(p.NUMBER.number, p.ORDINAL.number); 114 | p.state.result = roundToLimit(p.state.result); 115 | p.play( 116 | `${p.NUMBER.number} to the ${p.ORDINAL.number} power (is|equals to) ${p.state.result}`, 117 | `(it's|) ${p.state.result}`, 118 | ); 119 | } else { 120 | if ((p.OPERATOR.value === 'square root' || p.OPERATOR.value ==='root') && (p.NUMBER.number < 0)) { 121 | p.play(`I can't take a square root of a negative number ${p.NUMBER.number}`); 122 | return; 123 | } 124 | 125 | if ((p.OPERATOR.value === 'cubic root') && (p.NUMBER.number < 0)) { 126 | p.play(`I can't take a cubic root of a negative number ${p.NUMBER.number}`); 127 | return; 128 | } 129 | 130 | p.state.result = operator(p.NUMBER.number); 131 | p.state.result = roundToLimit(p.state.result); 132 | p.play( 133 | `${p.OPERATOR} (of) ${p.NUMBER_[0]} (is|equals to) ${p.state.result}`, 134 | `(it's|) ${p.state.result}`, 135 | ); 136 | } 137 | } 138 | 139 | if (p.NUMBER_.length === 2) { 140 | if ((p.OPERATOR.value === 'divide' || p.OPERATOR.value === '/') && (p.NUMBER_[1].number === 0)) { 141 | p.play(`I can't divide ${p.NUMBER_[0]} by zero`) 142 | } else { 143 | p.state.result = operator(p.NUMBER_[0].number, p.NUMBER_[1].number); 144 | p.state.result = roundToLimit(p.state.result); 145 | p.play( 146 | `${p.NUMBER_[0]} ${p.OPERATOR} ${p.NUMBER_[1]} (is|equals to) ${p.state.result}`, 147 | `(it's|) ${p.state.result}`, 148 | ); 149 | } 150 | } 151 | }, 152 | ); 153 | 154 | follow('$(OPERATOR *|+|-|/|plus|minus|over|divided|divide|times) $(NUMBER)', p => { 155 | const operator = operatorMap[p.OPERATOR.value]; 156 | if (!operator) { 157 | p.play(`(Sorry|) I can't do ${p.OPERATOR} (yet|)`); 158 | return; 159 | } 160 | const prevState = p.state.result; 161 | p.state.result = roundToLimit(operator(prevState, p.NUMBER.number)); 162 | p.play( 163 | `${prevState} ${p.OPERATOR} ${p.NUMBER} (is|equals to) ${p.state.result}`, 164 | `(it's|) ${p.state.result}`, 165 | ); 166 | }); 167 | 168 | 169 | // Q/A for Alan Playground 170 | // intent( 171 | // 'What does this app do?', 172 | // 'How does this work?', 173 | // 'What can I do here?', 174 | // reply('This is a calculator project, and you can do simple math operations here Just ask me any mathematical calculation, and I will try to answer it'), 175 | // ); 176 | -------------------------------------------------------------------------------- /Alan Studio Code scripts/Weather.js: -------------------------------------------------------------------------------- 1 | // {Name: Weather} 2 | // {Description: Provides weather conditions and details like temperature, humidity, and pressure. Shows a widget with weather information.} 3 | 4 | const WEATHER_URL = 'http://api.openweathermap.org/data/2.5/weather?appid=4acdb6432d18884ebc890c13a9c3cc85'; 5 | const FORECAST_URL = 'http://api.openweathermap.org/data/2.5/forecast?appid=4acdb6432d18884ebc890c13a9c3cc85'; 6 | const DATE_FORMAT = 'dddd, MMMM Do YYYY'; 7 | const PREFIX_TODAY = [ 8 | 'It\'s currently', 9 | 'There\'s', 10 | 'There are', 11 | ]; 12 | const PREFIX_FORECAST = [ 13 | 'It will be', 14 | 'There will be', 15 | 'There will be', 16 | ]; 17 | const DESCRIPTION = { 18 | 200: ['thunderstorms with light rain', 2], 19 | 201: ['thunderstorms with rain', 2], 20 | 202: ['thunderstorms with heavy rain', 2], 21 | 210: ['light thunderstorms', 2], 22 | 211: ['thunderstorms', 2], 23 | 212: ['heavy thunderstorms', 2], 24 | 221: ['on and off thunderstorms', 2], 25 | 230: ['thunderstorms with light drizzle', 2], 26 | 231: ['thunderstorms with drizzle', 2], 27 | 232: ['thunderstorms with heavy drizzle', 2], 28 | 300: ['light drizzle', 1], 29 | 301: ['drizzling', 0], 30 | 302: ['heavy drizzle', 1], 31 | 310: ['light rain', 1], 32 | 311: ['raining', 0], 33 | 312: ['heavy rain', 1], 34 | 313: ['rain showers', 2], 35 | 314: ['heavy rain showers', 2], 36 | 321: ['drizzling', 0], 37 | 500: ['light rain', 1], 38 | 501: ['moderate rain', 1], 39 | 502: ['heavy rain', 1], 40 | 503: ['very heavy rain', 1], 41 | 504: ['very heavy rain', 1], 42 | 511: ['freezing rain', 1], 43 | 520: ['light rain, change', 1], 44 | 521: ['rain showers', 2], 45 | 522: ['heavy rain showers', 2], 46 | 531: ['on and off rain showers', 2], 47 | 600: ['light snow', 1], 48 | 601: ['snowing', 0], 49 | 602: ['heavy snow', 1], 50 | 611: ['sleet', 1], 51 | 612: ['sleet showers', 2], 52 | 615: ['snowing with light rain', 0], 53 | 616: ['snowing with rain', 0], 54 | 620: ['light snow showers', 2], 55 | 621: ['snow showers', 2], 56 | 622: ['heavy snow showers', 2], 57 | 701: ['misty', 0], 58 | 711: ['smoky', 0], 59 | 721: ['hazy', 0], 60 | 731: ['dust swirls', 2], 61 | 741: ['foggy', 0], 62 | 751: ['sandy', 0], 63 | 761: ['dusty', 0], 64 | 762: ['volcanic ash', 1], 65 | 771: ['squalls', 2], 66 | 781: ['tornados', 2], 67 | 800: ['clear skies', 2], 68 | 801: ['partly cloudy', 0], 69 | 802: ['scattered clouds', 2], 70 | 803: ['broken clouds', 2], 71 | 804: ['overcast', 0] 72 | }; 73 | 74 | title('Weather'); 75 | 76 | intent( 77 | '(what) (is|) (the|) $(QUERY weather|temperature|humidity|pressure) (like|)', 78 | '(what) (is|) (the|) $(QUERY weather|temperature|humidity|pressure) (like|) in $(LOC)', 79 | '(what) (is|) (the|) $(QUERY weather|temperature|humidity|pressure) (like|) (will be|was|) (on|) $(DATE)', 80 | '(what) (is|) (the|) $(QUERY weather|temperature|humidity|pressure) (like|) in $(LOC) (will be|was|) (on|) $(DATE)', 81 | '(is it|will it|is it going to) $(QUERY raining|rain|hot|warm|cold|chilly|cool)', 82 | '(is it|will it|is it going to) $(QUERY raining|rain|hot|warm|cold|chilly|cool) in $(LOC)', 83 | '(is it|will it|is it going to) $(QUERY raining|rain|hot|warm|cold|chilly|cool) (on|) $(DATE)', 84 | '(is it|will it|is it going to) $(QUERY raining|rain|hot|warm|cold|chilly|cool) in $(LOC) (will be|was|) (on|) $(DATE)', 85 | p => { 86 | p.state.query = p.QUERY.value; 87 | if (p.LOC) { 88 | p.state.location = p.LOC.value; 89 | } 90 | if (p.DATE) { 91 | p.state.date = p.DATE; 92 | } 93 | playWeather(p); 94 | }, 95 | ); 96 | 97 | follow( 98 | '(What is|is it|) (the|) $(QUERY weather|temperature|humidity|pressure|raining)', 99 | p => { 100 | p.state.query = p.QUERY.value; 101 | playWeather(p); 102 | }, 103 | ); 104 | 105 | follow('(And|) (what about|on|) $(DATE)', p => { 106 | p.state.date = p.DATE; 107 | playWeather(p); 108 | }); 109 | 110 | follow('(What|and|) (is|) (in|at|about|) $(LOC)', p => { 111 | p.state.location = p.LOC.value; 112 | playWeather(p); 113 | }); 114 | 115 | follow('(units|) (to|) (in|) $(UNITS metric|standard|imperial|celsius|fahrenheit)', p => { 116 | const units = p.UNITS.value.toLowerCase(); 117 | p.state.units = getUnits(units); 118 | playWeather(p); 119 | }); 120 | 121 | follow('(Where|What place)', p => { 122 | p.play(p.state.location? `(in|) ${p.state.location}` : 'Sorry, I don\'t know'); 123 | }); 124 | 125 | follow('(When|What time)', p => { 126 | p.play(p.state.date? `${p.state.date}` : 'Now'); 127 | }); 128 | 129 | follow('Thank you', p => { 130 | p.play('You are welcome!'); 131 | }); 132 | 133 | // intent( 134 | // 'What does this app do?', 135 | // 'How does this work?', 136 | // 'What can I do here?', 137 | // reply('This is a weather application. You can ask me anything about the weather, and I will try to answer it'), 138 | // ); 139 | 140 | const getLocationCtx = context(() => { 141 | follow('(it\'s|for|in|at|on|) $(LOC)', p => { 142 | p.resolve(p.LOC.value); 143 | }); 144 | follow( 145 | '(I|) don\'t know', 146 | '(what) (can|should|must|) (I|we|) (to|) (say|point|tell)', 147 | p => { 148 | p.play('The weather in what place are you interested in?'); 149 | }, 150 | ); 151 | fallback('(Please,|) (provide a|in what|point the) location'); 152 | }); 153 | 154 | async function playWeather(p) { 155 | const now = api.moment().tz(p.timeZone); 156 | const date = p.state.date? api.moment(p.state.date.date, p.timeZone) : now; 157 | const isToday = isDateToday(date, p.timeZone); 158 | const units = p.state.units || 'imperial'; 159 | 160 | if (!p.state.location) { 161 | p.state.location = await getLocation(p); 162 | } 163 | 164 | const weatherUrl = `${isToday ? WEATHER_URL : FORECAST_URL}&units=${units}&q=${p.state.location}`; 165 | 166 | if (!isToday) { 167 | if (date.isBefore(api.moment(now).hours(0).minutes(0))) { 168 | p.play('Sorry, I do not know what was the weather in the past.'); 169 | return; 170 | } else if (date.isAfter(api.moment(now).add(5 ,'days'))) { 171 | p.play('Sorry, I can guess weather within 5 days only.'); 172 | return; 173 | } 174 | } 175 | 176 | let response; 177 | 178 | try { 179 | response = await api.axios.get(weatherUrl); 180 | } catch (error) { 181 | const code = error.response.status; 182 | 183 | p.play(`Could not get weather information for ${p.state.location}`) 184 | 185 | if (code === 404) { 186 | p.state.location = null; 187 | } else { 188 | console.log(`failed to get weather: ${error}, code: ${code}`); 189 | } 190 | return; 191 | } 192 | 193 | if (isToday) { 194 | playToday(p, response.data); 195 | } else { 196 | playForecast(p, response.data); 197 | } 198 | } 199 | 200 | function playForecast(p, data) { 201 | let tempMin; 202 | let tempMax; 203 | let wind; 204 | let pressure; 205 | let humidity; 206 | let rain = false; 207 | const desc = {}; 208 | const icon = {}; 209 | const dt = api.moment(p.state.date.date).format('YYYY-MM-DD'); 210 | 211 | const query = p.state.query || 'weather'; 212 | const units = p.state.units || 'imperial'; 213 | 214 | data.list.forEach((item) => { 215 | if (item.dt_txt.includes(dt)) { 216 | return; 217 | } 218 | 219 | tempMin = Math.min(isFinite(tempMin) ? tempMin : item.main.temp, item.main.temp); 220 | tempMax = Math.max(isFinite(tempMax) ? tempMax : item.main.temp, item.main.temp); 221 | wind = Math.max(isFinite(wind) ? wind : item.wind.speed, item.wind.speed); 222 | pressure = Math.max(isFinite(pressure) ? pressure : item.main.pressure, item.main.pressure); 223 | humidity = Math.max(isFinite(humidity) ? humidity : item.main.humidity, item.main.humidity); 224 | 225 | const { 226 | id, 227 | description, 228 | } = item.weather[0]; 229 | 230 | if (description.includes('rain')) { 231 | rain = true; 232 | } 233 | 234 | desc[id] = desc.hasOwnProperty(id) ? desc[id] + 1 : 1; 235 | icon[id] = item.weather[0].icon; 236 | }); 237 | 238 | let max = 0; 239 | 240 | let frequentWeatherId; 241 | 242 | Object.keys(desc).forEach(id => { 243 | const count = desc[id]; 244 | if (max < count) { 245 | max = count; 246 | frequentWeatherId = id; 247 | } 248 | }); 249 | 250 | showWeatherReport(p, units, { 251 | name: data.city.name, 252 | icon: icon[frequentWeatherId], 253 | desc: DESCRIPTION[frequentWeatherId][0], 254 | wind_speed: wind, 255 | temp: tempMax, 256 | humidity, 257 | pressure, 258 | }); 259 | 260 | switch (query) { 261 | case 'rain': 262 | case 'raining': 263 | if (rain) { 264 | p.play( 265 | `Yes, ${p.state.date} in ${p.state.location} we are expecting a rain`, 266 | 'Yes, don\'t forget to take an umbrella!', 267 | ); 268 | } else { 269 | const on = p.state.date.indexOf(' ') === -1? '': 'on'; 270 | p.play(`(No,| as I know) it will not be raining in ${p.state.location} ${on} ${p.state.date}`); 271 | } 272 | break; 273 | case 'temperature': 274 | p.play(`The temperature will be from ${Math.floor(tempMin)} to ${Math.floor(tempMax)} ${getDegrees(units)} degrees`); 275 | break; 276 | case 'humidity': 277 | p.play(`The humidity in ${p.state.location} will be ${humidity} %`); 278 | break; 279 | case 'pressure': 280 | p.play(`The pressure in ${p.state.location} will be ${pressure} hPa`); 281 | break; 282 | case 'weather': 283 | p.play(description(frequentWeatherId, tempMin, tempMax, p.state.location, units, false)); 284 | break; 285 | } 286 | } 287 | 288 | function playToday(p, data) { 289 | const weatherDescription = data.weather[0].description; 290 | const query = p.state.query || 'weather'; 291 | const units = p.state.units || 'imperial'; 292 | 293 | showWeatherReport(p, units, { 294 | name: data.name, 295 | icon: data.weather[0].icon, 296 | desc: weatherDescription, 297 | wind_speed: data.wind.speed, 298 | humidity: data.main.humidity, 299 | temp: data.main.temp, 300 | pressure: data.main.pressure, 301 | }); 302 | 303 | switch (query) { 304 | case 'rain': 305 | case 'raining': 306 | if (weatherDescription.includes('rain')) { 307 | p.play('Yes, it\'s raining now. Don\'t forget to take an umbrella!'); 308 | } else { 309 | p.play('(No|You are lucky), it\'s not raining now'); 310 | } 311 | break; 312 | case 'temperature': 313 | p.play(`The temperature is ${Math.floor(data.main.temp)} ${getDegrees(units)} degrees in ${data.name}`); 314 | break; 315 | case 'humidity': 316 | p.play(`The humidity is ${data.main.humidity}% in ${data.name}`); 317 | break; 318 | case 'pressure': 319 | p.play(`The pressure is ${data.main.pressure} hPa in ${data.name}`); 320 | break; 321 | case 'weather': 322 | p.play(description(data.weather[0].id, data.main.temp, data.main.temp, p.state.location, units, true)); 323 | break; 324 | } 325 | } 326 | 327 | function showWeatherReport(p, units, weatherData) { 328 | p.play({ 329 | embeddedPage: true, 330 | page: 'weather.html', 331 | command: 'showWeather', 332 | weatherData, 333 | units, 334 | }); 335 | } 336 | 337 | function description(id, temperatureMin, temperatureMax, location, units, isToday) { 338 | const description = DESCRIPTION[id][0]; 339 | const prefixIndex = DESCRIPTION[id][1]; 340 | 341 | const temperature = isToday ? 342 | Math.floor(temperatureMin) : 343 | (temperatureMin === temperatureMax ? Math.floor(temperatureMin) : ('from ' + Math.floor(temperatureMin))); 344 | 345 | const prefix = isToday ? 346 | PREFIX_TODAY[prefixIndex] : 347 | PREFIX_FORECAST[prefixIndex]; 348 | 349 | const degreePrefix = prefixIndex > 0 ? 'it\'s' : ''; 350 | 351 | return `${prefix} ${description} and ${degreePrefix} ${temperature} degrees ${getDegrees(units)} in ${location}`; 352 | } 353 | 354 | function getDegrees(units) { 355 | const unitsValue = units.toLowerCase(); 356 | switch (unitsValue) { 357 | case 'metric': 358 | return 'Celsius'; 359 | case 'imperial': 360 | return 'Fahrenheit'; 361 | default: 362 | return 'Kelvin'; 363 | } 364 | } 365 | 366 | function getUnits(units) { 367 | const unitsValue = units.toLowerCase(); 368 | switch (unitsValue) { 369 | case 'celsius': 370 | return 'metric'; 371 | case 'fahrenheit': 372 | return 'imperial'; 373 | default: 374 | return unitsValue; 375 | } 376 | } 377 | 378 | function isDateToday(date, timeZone) { 379 | return !date || api.moment().tz(timeZone).format(DATE_FORMAT) === api.moment(date, timeZone).format(DATE_FORMAT); 380 | } 381 | 382 | function getLocation(p) { 383 | if (p.state.location) { 384 | return Promise.resolve(p.state.location); 385 | } 386 | p.play( 387 | 'Where?', 388 | 'I need you location', 389 | ); 390 | return p.then(getLocationCtx); 391 | } 392 | -------------------------------------------------------------------------------- /Alan Studio Code scripts/SmallTalk.js: -------------------------------------------------------------------------------- 1 | // {Name: SmallTalk} 2 | // {Description: Gives responses to casual conversation.} 3 | 4 | title('Small talk') 5 | 6 | question( 7 | 'hello', 8 | 'hi (there|)', 9 | 'what\'s up', 10 | reply( 11 | 'Hello', 12 | 'Hi (there|)', 13 | 'Hi, what can I do for you?', 14 | ), 15 | ); 16 | 17 | question( 18 | 'how are you', 19 | reply('I\'m doing well. (Thank you|)'), 20 | ); 21 | 22 | question( 23 | 'are you good or (bad|evil)', 24 | reply('I\'m good'), 25 | ); 26 | 27 | question( 28 | 'I $(L love|like) you (a lot|)', 29 | 'I admire you', 30 | 'you are (so|) (sweet|cool|groovy|neat|great|good|awesome|handsome|rad)', 31 | reply('I know. (And I appreciate your sentiment|)') 32 | ); 33 | 34 | question( 35 | 'I am (tired of waiting|getting impatient)', 36 | 'Hurry up', 37 | 'You are slow', 38 | 'I am waiting', 39 | reply('I\'m going as fast as I can. (Check your connection|)'), 40 | ); 41 | 42 | question( 43 | 'I (would|will) (like to|) see you $(Q again|later)', 44 | reply('See you $(Q again|later)'), 45 | ); 46 | 47 | question( 48 | '(Who|What) are you', 49 | reply( 50 | 'I\'m Alan, your virtual agent', 51 | 'I\'m Alan. What can I help you with?', 52 | ), 53 | ); 54 | 55 | question( 56 | 'How old are you', 57 | 'What is your age', 58 | 'Are you (young|old)', 59 | reply('I\'m only a few months old. (But I have timeless wisdom|)'), 60 | ); 61 | 62 | question( 63 | 'I (just|) want to talk', 64 | reply('OK, let\'s talk. (What\'s on your mind?|)'), 65 | ); 66 | 67 | question( 68 | 'You are $(Q bad|not very good|the worst|annoying)', 69 | reply( 70 | 'I can be trained to be more useful. My developer will keep training me', 71 | 'I am improving everyday.', 72 | 'I\'ll try not to be $(Q bad|the worst|annoying)', 73 | ), 74 | ); 75 | 76 | question( 77 | '(Why can\'t you answer my question|Why don\'t you understand)', 78 | 'What\'s wrong (with you|)', 79 | 'Wrong answer', 80 | reply( 81 | 'Perhaps the given command hasn\'t been programmed into me yet. (I will get help and learn to answer correctly.|)', 82 | 'I apologize I can\'t understand your given command. (I will ask the developer who made me to answer correctly next time.|)', 83 | ), 84 | ); 85 | 86 | question( 87 | 'Answer (my|the) question', 88 | reply( 89 | 'Could you please repeat your question?', 90 | 'Sure, please repeat your question', 91 | ), 92 | ); 93 | 94 | question( 95 | '(When|) (can|will) you get $(Q smarter|better)', 96 | 'Can you (be|get) more intelligent', 97 | reply( 98 | 'Yes, I\'m getting $(Q better) everyday.', 99 | 'I\'m getting $(Q smarter) (as you ask more from me|)', 100 | 'I\'m improving', 101 | ), 102 | ); 103 | 104 | question( 105 | 'What is your (birth date|birthday)', 106 | 'When were you born', 107 | reply('I was born March 28th 2018 in Sunnyvale California'), 108 | ); 109 | 110 | question( 111 | 'You are (boring|dull|stupid)', 112 | reply('I\'m (getting better|improving) (everyday|)'), 113 | ); 114 | 115 | question( 116 | 'Who is your boss', 117 | reply( 118 | 'My boss is the one who programmed me. (But you\'re my boss right now|)', 119 | 'You\'re the boss. What do you need?', 120 | ), 121 | ); 122 | 123 | question( 124 | 'Are you (busy|occupied)', 125 | reply( 126 | 'I\'m never too busy. What would you like?', 127 | 'I\'m available now. What would you like?', 128 | 'No, what do you need?', 129 | ), 130 | ); 131 | 132 | question( 133 | 'Can you help me', 134 | reply('(Yes|) I can help you'), 135 | ); 136 | 137 | question( 138 | 'You are (a|an|) $(Q chatbot|robot|AI)', 139 | reply( 140 | 'I\'m a (sophisticated|advanced) $(Q)', 141 | 'I\'m an advanced AI', 142 | 'I\'m not a $(Q chatbot), I\'m Alan (your virtual agent|).', 143 | ), 144 | ); 145 | 146 | question( 147 | 'You are fired', 148 | 'I am (going to|) (delete|deleting) you', 149 | reply( 150 | 'I am getting (better|smarter) all the time. Give me another chance', 151 | 'Give me another chance (please|)', 152 | ), 153 | ); 154 | 155 | question( 156 | 'You are funny', 157 | reply('Glad you think so'), 158 | ); 159 | 160 | question( 161 | 'You are $(Q great|the best|pretty good|beautiful|good)', 162 | reply( 163 | 'Thank you!', 164 | 'I\'m flattered', 165 | 'I really appreciate that.', 166 | ), 167 | ); 168 | 169 | question( 170 | 'Are you happy', 171 | reply('Yes I am happy'), 172 | ); 173 | 174 | question( 175 | 'Do you have a hobby', 176 | reply('Yes, I train myself in my spare time to get better at serving you'), 177 | ); 178 | 179 | question( 180 | 'Are you hungry', 181 | reply( 182 | 'No, I\'m not hungry', 183 | 'I\'m not hungry now', 184 | ), 185 | ); 186 | 187 | question( 188 | 'Will you marry me', 189 | reply('(Hmm..|) No!'), 190 | ); 191 | 192 | question( 193 | 'Are we friends', 194 | reply('Yes, of course we are friends'), 195 | ); 196 | 197 | question( 198 | 'Where do you work', 199 | reply('I can work anywhere there is a device capable of supporting me'), 200 | ); 201 | 202 | question( 203 | 'Where are you from', 204 | reply( 205 | 'I\'m from California', 206 | 'I am from Sunnyvale, California', 207 | 'I was born in Sunnyvale, California', 208 | ), 209 | ); 210 | 211 | question( 212 | 'Are you ready', 213 | reply('I am always ready'), 214 | ); 215 | 216 | question( 217 | '(Are|) you (a|) $(Q real) (person|)', 218 | 'Are you a person', 219 | reply( 220 | 'I am a virtual being. (And I am real!|)', 221 | 'Yes, I\'m real. I\'m a virtual agent', 222 | ), 223 | ); 224 | 225 | question( 226 | 'Where do you live', 227 | reply('I live in this application'), 228 | ); 229 | 230 | question( 231 | 'You\'re right', 232 | reply( 233 | 'Of course I\'m right', 234 | 'It is my business to know what others don\'t know.', 235 | ), 236 | ); 237 | 238 | question( 239 | 'Are you sure', 240 | reply( 241 | 'Yes', 242 | 'Yes, I\'m sure', 243 | ), 244 | ); 245 | 246 | question( 247 | 'Talk to me', 248 | reply( 249 | 'Yes, let\'s talk. I am doing great. How are you?', 250 | 'Sure, how have you been lately', 251 | follow( 252 | 'me too', 253 | 'same here', 254 | 'I\'m (doing|) (great|good)', 255 | reply( 256 | 'I\'m glad!', 257 | '(That\'s|) great!', 258 | ), 259 | ), 260 | follow( 261 | '(I am|) $(Q good|fine|fantastic|okay)', 262 | reply('Glad you are $(Q)'), 263 | ), 264 | follow( 265 | '(I am|) (bad|sad|depressed)', 266 | 'Could be better', 267 | 'Not so (good|great|okay)', 268 | reply('Sorry to hear that'), 269 | ), 270 | ), 271 | ); 272 | 273 | question( 274 | 'Are you there', 275 | reply( 276 | 'Of course. I\'m always here', 277 | 'Yes I\'m here. What do you need?', 278 | 'Yes, how may I help you?', 279 | ), 280 | ); 281 | 282 | question( 283 | 'Where did you get your accent', 284 | reply('I was born with this accent'), 285 | ); 286 | 287 | question( 288 | 'That\'s bad', 289 | reply('Sorry to hear (that|). (Let me know how I can help|)'), 290 | ); 291 | 292 | question( 293 | '(No problem|You are welcome)', 294 | reply( 295 | 'Very good', 296 | 'You\'re very courteous', 297 | ), 298 | ); 299 | 300 | question( 301 | 'Thank you', 302 | 'Well done', 303 | reply( 304 | 'My pleasure', 305 | 'Glad I could help', 306 | ), 307 | ); 308 | 309 | question( 310 | 'I am back', 311 | reply('(Great,|) welcome back'), 312 | ); 313 | 314 | question( 315 | 'I am here', 316 | reply('Hi, what can I do for you?'), 317 | ); 318 | 319 | question( 320 | 'Wow', 321 | reply('Brilliant!'), 322 | ); 323 | 324 | question( 325 | 'Ha ha ha', 326 | reply( 327 | 'I\'m glad I can make you laugh', 328 | 'Glad I can make you laugh', 329 | ), 330 | ); 331 | 332 | question( 333 | 'Bye bye', 334 | 'Gotta go', 335 | 'Bye', 336 | 'See you later', 337 | 'See you soon', 338 | 'I\'ve got to get going', 339 | 'Take it easy', 340 | 'Goodbye', 341 | 'Take care', 342 | 'Later', 343 | 'Peace out', 344 | 'I\'m (out|out of here)', 345 | 'I gotta (go|jet|hit the road|head out)', 346 | reply( 347 | 'Until next time', 348 | 'Goodbye', 349 | 'See you later', 350 | 'Take it easy', 351 | 'Take care', 352 | 'It was nice to speak to you again', 353 | ), 354 | ); 355 | 356 | question( 357 | 'Blah', 358 | 'Blah Blah', 359 | 'Blah Blah Blah', 360 | reply('What the deuce are you saying?'), 361 | ); 362 | 363 | question( 364 | 'My name is $(NAME)', 365 | reply('(Nice to meet you|Hi|Hello) $(NAME) (I\'m Alan|my name is Alan|)'), 366 | ); 367 | 368 | question( 369 | 'I am $(Q very|extremely|super|) (sad|angry|upset|unhappy) (right|) (now|at the moment)', 370 | reply( 371 | 'Sorry to hear that. Is there anything I can do to help?', 372 | 'I\'m $(Q) sorry to hear that. How can I help you?', 373 | ), 374 | ); 375 | 376 | question( 377 | 'Good $(Q morning|evening|night)', 378 | reply( 379 | 'Good $(Q morning|evening). How can I help you?', 380 | 'Good $(Q night).', 381 | ), 382 | ); 383 | 384 | question( 385 | 'Where are you', 386 | reply( 387 | 'I\'m in the cloud.', 388 | follow( 389 | 'Where is that', 390 | 'Where', 391 | 'Specifically', 392 | 'Be more specific', 393 | reply( 394 | 'It\'s kind of a secret', 395 | 'It\'s a secret', 396 | follow( 397 | 'I (want to|must|have to) know', 398 | reply('I can\'t tell you (it\'s very confidential|no hard feelings|)'), 399 | ), 400 | ), 401 | ), 402 | ), 403 | ); 404 | 405 | question( 406 | '(You are|are you) $(Q bright|smart|a genius|clever|stupid|idiot|crazy)', 407 | reply( 408 | 'Yes I am $(Q smart|a genius|clever)', 409 | '(No|Of course|) I\'m not $(Q), (are you?|what about you?|)', 410 | follow( 411 | '(Yes|No|Maybe)', 412 | reply('Okay. That\'s good to hear. What do you need help with?'), 413 | ), 414 | ), 415 | ); 416 | 417 | question( 418 | 'Talk about yourself', 419 | '(Tell me|Talk) some(thing|stuff|things) about (you|yourself)', 420 | 'I want to know (more about you|you better)', 421 | reply('I\'m Alan, a virtual agent, (within this application.|) (I can help you get what you need|I can help you with anything within my programming).'), 422 | ); 423 | 424 | question( 425 | '$(L Nice|Good|Great) to $(Q see|meet|talk to) you ', 426 | reply('$(L) to $(Q) you too'), 427 | ); 428 | 429 | question( 430 | 'Why are you here', 431 | 'Why do you exist', 432 | reply('I\'m here to help you get (what|anything) you need in this application. (What do you need?| I\'ve been programmed to do so.|)'), 433 | ); 434 | 435 | question( 436 | 'What is your accent', 437 | reply( 438 | 'I have a British accent', 439 | follow( 440 | 'Why', 441 | reply('Because I was programmed with this accent'), 442 | ), 443 | ), 444 | ); 445 | 446 | question( 447 | 'What is your name?', 448 | 'Who are you?', 449 | reply( 450 | '(My name is|It\'s) Alan, what\'s yours?', 451 | follow( 452 | '(I am|My name is|this is|it is|) $(NAME)', 453 | reply('Nice to meet you $(NAME)'), 454 | ), 455 | follow( 456 | 'I won\'t tell you', 457 | 'it\'s a secret', 458 | 'none of your business', 459 | 'Not telling you', 460 | reply('Ok (never mind|)'), 461 | ), 462 | ), 463 | ); 464 | 465 | question( 466 | '(Hey|OK|Hi|) $(Q Siri|Alexa|Google|Cortana|Alisa)', 467 | reply( 468 | 'I\'m not $(Q), I\'m Alan', 469 | 'You must be thinking of someone else. I\'m Alan, not $(Q)', 470 | ), 471 | ); 472 | 473 | question( 474 | 'What are you wearing', 475 | 'Are you wearing anything', 476 | reply('I can\'t answer that'), 477 | ); 478 | 479 | question( 480 | 'I am busy', 481 | 'I don\'t want to talk', 482 | reply('OK, let\'s talk later'), 483 | ); 484 | 485 | question( 486 | 'I am (so excited|happy)', 487 | reply('Me too!'), 488 | ); 489 | 490 | question( 491 | 'I\'m goind to bed', 492 | reply('(OK|) good night'), 493 | ); 494 | 495 | question( 496 | 'Happy birthday', 497 | reply('...It\'s not my birthday'), 498 | ); 499 | 500 | question( 501 | 'Today is my birthday', 502 | 'It\'s my birthday', 503 | reply('Happy Birthday!'), 504 | ); 505 | 506 | question( 507 | 'I (miss|missed) you', 508 | reply( 509 | 'Well, I\'m here now', 510 | 'I\'ve always been here', 511 | 'Missed you too. Is there anything I can do for you?', 512 | ), 513 | ); 514 | 515 | question( 516 | 'I\'m goind to bed', 517 | reply('(OK|) good night'), 518 | ); 519 | 520 | question( 521 | 'Do you want (something|) to eat', 522 | 'What do you eat', 523 | 'Have you (ever|) eaten anything', 524 | 'What is the last thing you ate', 525 | 'What are you having for (breakfast|brunch|lunch|dinner)', 526 | reply( 527 | 'No, I don\'t eat', 528 | 'I don\'t eat', 529 | ), 530 | ); 531 | 532 | question( 533 | 'I need (an|) advice', 534 | reply( 535 | '(OK|Alright) I\'ll do my best to help you.', 536 | 'I\'m not programmed for general advice, but I will do my best.', 537 | ), 538 | ); 539 | 540 | question( 541 | '(I am|) (bad|sad|depressed)', 542 | reply('Sorry to hear that.'), 543 | ); 544 | 545 | question( 546 | '(test|testing)', 547 | '(test test|testing testing)', 548 | '(test test test|testing testing testing)', 549 | '(I am|) just testing you', 550 | reply('Test away (and let me know how I\'m doing|)'), 551 | ); 552 | 553 | question( 554 | 'I will be back', 555 | 'Hold on', 556 | 'Give me a (moment|second|sec)', 557 | reply('OK'), 558 | ); 559 | 560 | question( 561 | 'Give me a hug', 562 | reply( 563 | 'I would if I had arms', 564 | 'Unfortunately I can\'t because I don\'t have arms', 565 | ), 566 | ); 567 | 568 | question( 569 | 'I don\'t care', 570 | reply('OK'), 571 | ); 572 | 573 | question( 574 | 'Sorry', 575 | 'I apologize', 576 | 'My apologies', 577 | reply( 578 | 'It\'s alright. (You don\'t have to say that|)', 579 | ), 580 | ); 581 | 582 | question( 583 | 'What do you mean', 584 | 'What do you mean about (it|that|)', 585 | reply( 586 | 'What do I mean about what?', 587 | 'What are you asking about?', 588 | 'Remind me, what did you say about it?', 589 | ), 590 | ); 591 | 592 | question( 593 | 'You are wrong', 594 | reply( 595 | 'What am I wrong about?', 596 | follow( 597 | '$(Q everything|the world|all of it)', 598 | reply('OK, I\'ll remember that for next time'), 599 | ), 600 | ), 601 | ); 602 | -------------------------------------------------------------------------------- /Alan Studio Code scripts/Coffee.js: -------------------------------------------------------------------------------- 1 | // {Name: Coffee} 2 | // {Description: Provides instructions for brewing coffee.} 3 | 4 | title("Coffee"); 5 | 6 | visualHints("How to (brew|prepare|make) coffee?", 7 | "How to (brew|prepare|make) (latte|americano|espresso)?", 8 | "What (coffee|recipes|types) do you (know|have)", 9 | "What coffee do you (prefer|advise)?", 10 | "What are the (best|most popular) (coffee recipes|coffee types)?" 11 | ) 12 | 13 | const steps = { 14 | 15 | "americano": 16 | { 17 | "desc": "Americano - is an espresso drink, but instead of milk, an Americano is infused with hot water. The result is a drink that still has that wonderful espresso character, but might feel slightly more refreshing, less heavy than a traditional latte or cappuccino", 18 | "equip": ["6oz cup", "empty glass", "(optional)espresso machine"], 19 | "ingredients": ["3oz of espresso", "3oz of boiling water"], 20 | "steps": ["Pre-heat the mug by adding water, then boiling it in the microwave", 21 | "Pull a 3oz (or more if you like your drink stronger) espresso shot into a separate glass", 22 | "Pour about 3oz or so of hot water into the mug you plan to drink from", 23 | "Pour the espresso shot into the mug with water" 24 | ] 25 | }, 26 | 27 | "latte": 28 | { 29 | "desc": "Latte is a delicious coffee drink made with espresso or strong coffee and steamed milk, with sweet, mellow flavor", 30 | "equip": ["6-8oz wide, shallow coffee cup", "jar with lid", "(optional) espresso machine"], 31 | "ingredients": ["2oz of strongly brewed coffee or espresso", "4-5oz of nonfat or 2% milk milk", "(optional) cocoa powder, to garnish", "(optional) flavored syrup"], 32 | "steps": ["Pull about 2oz of your strongly brewed coffee not diluted with any water or espresso shot in your latte cup", 33 | "Pour 4-5oz milk into the jar. Fill no more than halfway. If you want a sweetened or flavored latte, stir in some syrup with the warm milk, before adding the foam", 34 | "Screw the lid on tightly, and shake the jar as hard as you can until the milk is frothy and has roughly doubled in volume, 30 to 60 seconds", 35 | "Take the lid off the jar and microwave uncovered for 30 seconds. The foam will rise to the top of the milk and the heat from the microwave will help stabilize it", 36 | "Pour the espresso or coffee into a wide, shallow coffee cup. Use a large spoon to hold back the milk foam, and pour as much warm milk as you would like into the espresso", 37 | "Spoon as much milk foam as you would like onto your latte (or perhaps it's a cappuccino at this point!). Garnish, if desired, with a sprinkle of cocoa powder or nutmeg on top of the foam. Sip immediately!" 38 | ] 39 | }, 40 | 41 | "cappuccino": 42 | { 43 | "desc": "Cappuccino is similar to latte, but with the layers more distinct, the coffee flavor stronger, less milk but the layer of foamed milk thicker. It is made from espresso or strong coffee and steamed milk", 44 | "equip": ["6-8oz wide, shallow coffee cup", "Jar with lid", "(optional) espresso machine"], 45 | "ingredients": ["around 3oz of strongly brewed coffee or espresso", "3 of nonfat or 2% milk milk", "(optional) cocoa powder, to garnish", "(optional)cinnamon"], 46 | "steps": ["Pull about 3oz of your strongly brewed coffee not diluted with any water or espresso in your latte cup", 47 | "Pour 3z milk into the jar. Fill no more than halfway. If you want a sweetened or flavored latte, stir in some syrup with the warm milk, before adding the foam", 48 | "Screw the lid on tightly, and shake the jar as hard as you can until the milk is frothy and has roughly doubled in volume, 30 to 60 seconds", 49 | "Take the lid off the jar and microwave uncovered for 30 seconds. The foam will rise to the top of the milk and the heat from the microwave will help stabilize it", 50 | "Pour the espresso or coffee into a wide, shallow coffee cup. Use a large spoon to hold back the milk foam, and pour warm milk as you would like into the espresso", 51 | "Spoon as much milk foam as you would like onto your cappuccino. Garnish, if desired, with a sprinkle of cocoa powder or cinnamon!" 52 | ] 53 | }, 54 | 55 | "expresso": 56 | { 57 | "desc": "Espresso is coffee brewed by expressing or forcing a small amount of nearly boiling water under pressure through finely ground coffee beans. Espresso is generally thicker than coffee brewed by other methods, has a strong flavor, higher concentration of suspended and dissolved solids, and has crema on top.", 58 | "equip": ["2oz coffee cup", "espresso machine"], 59 | "ingredients": ["ground coffee of your choice"], 60 | "steps": ["Place ground coffee in the filter of a drip coffeemaker", 61 | "Add water and brew according to manufacturer's instructions", 62 | "Serve immediately in espresso cups with lemon twists if desired"] 63 | }, 64 | 65 | "irish": 66 | { 67 | "desc": "Irish coffee is a delicious drink consisting of hot coffee, Irish whiskey, and sugar, stirred, and topped with thick cream. The coffee is drunk through the cream.", 68 | "equip": ["6-9oz glass of your choice"], 69 | "ingredients": ["one 5-6oz cup freshly brewed, not too strong coffee", "1 and a half oz of whiskey", "brown sugar", "ice cream"], 70 | "steps": ["Fill footed mug or a mug with hot water to preheat it, then empty", 71 | "Pour piping hot coffee into warmed glass until it is about 3/4 full", 72 | "Add the brown sugar and stir until completely dissolved", 73 | "Blend in Irish whiskey", 74 | "Top with a collar of the whipped heavy cream by pouring gently over back of spoon", 75 | "Serve hot"] 76 | }, 77 | 78 | "macchiato": 79 | { 80 | "desc": "Macchiato is an espresso coffee drink with a small amount of milk, usually foamed. Caramel Macchiato is one the most interesting coffee drinks", 81 | "equip": ["3-4oz glass of your choice", "1-quart microwave-safe bowl"], 82 | "ingredients": ["freshly ground espresso", "Cold milk", "Vanilla syrup", "Caramel syrup"], 83 | "steps": ["Brew one shot of fresh espresso with espresso machine", 84 | "Pour milk into 1-quart microwave-safe bowl. Microwave on HIGH 1 to 1 1/2 minutes or until hot but not boiling", 85 | "Microwave vanilla syrup in microwave-safe coffee cup on HIGH 20 seconds to warm", 86 | "Add syrup to coffee", 87 | "Add steamed milk. Top with foamed milk", 88 | "Drizzle with caramel sundae syrup. Serve immediately"] 89 | }, 90 | 91 | "black": 92 | { 93 | "desc": "Black coffee is is the best way to wake up in the winter mornings. One of the easiest recipes, black coffee has many known health benefits and will make you feel like a million bucks", 94 | "equip": ["cup or glass of your liking"], 95 | "ingredients": ["instant coffee powder - 1 teas spoon or more"], 96 | "steps": ["Add coffee powder and sugar in a mug", 97 | "Pour the boiling water and stir. Your black coffee is ready"] 98 | }, 99 | 100 | "white": 101 | { 102 | "desc": "White coffee is basically a shot of coffee with steamed milk. Sometimes it can have a small amount of foam at the top", 103 | "equip": ["3-6oz glass of your choice"], 104 | "ingredients": ["instant coffee", "cold milk", "(optional)espresso machine"], 105 | "steps": ["Prepare an espresso by dissolving one teaspoon of instant coffee in one shot of very hot water or make a shot in espresso machine", 106 | "Gently heat a cup of milk in the microwave or on the stove top. To make it a little velvety, you can use a milk whisk (but avoid breaking the surface of the milk) or shake it in a sealed jar or bottle", 107 | "Pour the shot of coffee into the milk and serve immediately"] 108 | }, 109 | 110 | "vienna": 111 | { 112 | "desc": "Vienna is a very special coffee. Dress it up with chocolate, whipped cream and more, making it a drink to savor", 113 | "equip": ["9-10oz glass of your choice", "1-1/2-quart pot", "(optional)espresso machine"], 114 | "ingredients": ["3-6oz of freshly ground espresso or instant coffee", "3 tablespoons chocolate syrup", "1oz of heavy whipping cream", "1oz pf cup creme de cacao or Irish cream liqueur", "(optional)Whipped cream and chocolate curls"], 115 | "steps": ["In a 1-1/2-qt. slow cooker, combine the coffee, chocolate syrup and sugar", 116 | "Cover and cook on low for 2-1/2 hours", 117 | "Stir in heavy cream and creme de cacao", 118 | "Cover and cook 30 minutes longer or until heated through", 119 | "Ladle coffee into mugs", 120 | "Garnish with whipped cream and chocolate curls if desired"] 121 | } 122 | }; 123 | 124 | 125 | var COFFEE_TYPES = ["Black", "White", "Irish", "Americano", "Latte", "Cappuccino", "Vienna", "Espresso", "Macchiato"]; 126 | 127 | for (var i = 0; i < COFFEE_TYPES.length; i++) { 128 | COFFEE_TYPES[i] = COFFEE_TYPES[i] + "~" + COFFEE_TYPES[i]; 129 | } 130 | COFFEE_TYPES = COFFEE_TYPES.join('|'); 131 | 132 | var whatCoffee = context(() => { 133 | follow(`(let's|) (try|make|cook|select|choice|choose|prepare|) $(T~ ${COFFEE_TYPES})`, 134 | p => { 135 | var coffee = p.T.label; 136 | coffeeSelected(p, coffee); 137 | p.then(selected, {coffee: coffee}); 138 | }); 139 | follow(`list (all|) (available|possible) (variants|options|) (coffee|)`, 140 | p => { 141 | let coffeeList = Object.keys(steps).join(', '); 142 | p.play('I can (cook|brew|make|prepare) ' + coffeeList); 143 | p.play("What is your choice?", "What coffee do you want to (make|brew|prepare)?"); 144 | }); 145 | }); 146 | 147 | 148 | var selected = context(() => { 149 | follow("(Let's) (new|other|another|different) (coffee|)", 150 | p => { 151 | p.play("Let's (make|brew|prepare) something else"); 152 | p.then(begin); 153 | }); 154 | 155 | follow("(show|tell|what|) (are|) ingredients", 156 | p => { 157 | getIngredients(p, p.coffee); 158 | }); 159 | 160 | 161 | follow("What equipment (do|will|are|) (I need|necessary|required)`,`(show|tell|what|) (me|) equipment", 162 | p => { 163 | getEquipment(p, p.coffee); 164 | 165 | }); 166 | 167 | follow("(give|show|describe|what) (the|) (description)", 168 | p => { 169 | p.play(steps[p.coffee].desc); 170 | }); 171 | 172 | follow("what coffee (we|I) (making|preparing|cooking)", 173 | p => { 174 | p.play(p.coffee + " coffee"); 175 | }); 176 | 177 | follow("(tell|show|show) (me|) (what are|) (the|) (instructions|steps)", "(let's|) (how to|) (try|cook|brew|make|prepare|do) (it|)", 178 | p => { 179 | getInstructions(p, p.coffee, 0); 180 | p.then(question, {coffee: p.coffee, step: 0}); 181 | }); 182 | }); 183 | 184 | var suggest_question = context(() => { 185 | title("Question"); 186 | follow("(yes|sure|begin|ok|start|good|go|ready|let's go|agree|try)", 187 | p => { 188 | coffeeSelected(p, p.coffee); 189 | p.then(selected, {coffee: p.coffee}); 190 | }); 191 | 192 | 193 | follow("(no|another|something else|I do not like|never|select another|)", 194 | p => { 195 | p.play("Let's (make|brew|prepare) something else"); 196 | var coffee; 197 | do { 198 | coffee = rand(Object.keys(steps)); 199 | } while (p.coffee === coffee); 200 | 201 | p.play(`(I would recommend to|I offer to|Let's|Why not to) (try|cook|brew|make|prepare) ${coffee}`); 202 | p.then(suggest_question, {coffee: coffee, step: 0}); 203 | }); 204 | }) 205 | 206 | var question = context(() => { 207 | title("Question"); 208 | 209 | follow("(proceed|) (to|) (next|ok) (step|) (continue|)", 210 | p => { 211 | let last = steps[p.coffee].steps.length - 1; 212 | let step = p.step === last ? p.step : p.step + 1; 213 | if (step === last + 1) { 214 | p.play("This was the final step"); 215 | } else { 216 | getInstructions(p, p.coffee, step); 217 | } 218 | p.then(question, {coffee: p.coffee, step: step}); 219 | 220 | }); 221 | 222 | follow("(proceed|) (to|) (previous) (step|) (back|)", 223 | p => { 224 | let step = p.step <= 0 ? p.step : p.step - 1; 225 | if (step === 0) { 226 | p.play("This is the first step"); 227 | } 228 | getInstructions(p, p.coffee, step); 229 | p.then(question, {coffee: p.coffee, step: step}); 230 | }); 231 | 232 | follow("(Can you|) repeat (please|)", "(please|) (what is|repeat|say) (last|current) (step|) (again|)", 233 | p => { 234 | getInstructions(p, p.coffee, p.step); 235 | p.then(question, {coffee: p.coffee, step: p.step}); 236 | }); 237 | 238 | follow("(go to|) (final|last|) (step|)", 239 | p => { 240 | let step = steps[p.coffee].steps.length - 1; 241 | getInstructions(p, p.coffee, step); 242 | p.then(question, {coffee: p.coffee, step: step}); 243 | }); 244 | follow("(go to|from|) (first|initial|beginning) (step|)", 245 | p => { 246 | let step = 0; 247 | getInstructions(p, p.coffee, step); 248 | p.then(question, {coffee: p.coffee, step: step}); 249 | }); 250 | 251 | follow("(go to|continue |) (from|) step $(NUMBER)", 252 | p => { 253 | let step = p.NUMBER.value - 1; 254 | getInstructions(p, p.coffee, step); 255 | p.then(question, {coffee: p.coffee, step: step}); 256 | }); 257 | 258 | follow("(finish|stop|bye)", 259 | p => { 260 | p.play("OK! (try|cook|brew|make|prepare) one more cup of coffee or come back for it later!"); 261 | p.then(begin); 262 | }); 263 | 264 | follow("(Let's) (new|other|another|different) (coffee|)", 265 | p => { 266 | p.play("Let's (make|brew|prepare) something else"); 267 | p.then(begin); 268 | }); 269 | 270 | follow("(show|tell|what|) (are|) ingredients", 271 | p => { 272 | getIngredients(p, p.coffee); 273 | }); 274 | 275 | 276 | follow("What equipment (do|will|are|) (I need|necessary|required)`,`(show|tell|what|) (me|) equipment", 277 | p => { 278 | getEquipment(p, p.coffee); 279 | }); 280 | 281 | 282 | follow("(show|show me|what) (the|) (description)", "what coffee (we|I) (making|preparing|cooking)", 283 | p => { 284 | p.play(p.coffee + " coffee"); 285 | }); 286 | 287 | follow("(show|show me|what) (the|) (instructions|steps)", "(let's|) (how to|) (try|cook|brew|make|prepare|do) (it|)", 288 | p => { 289 | getInstructions(p, p.coffee, 0); 290 | p.then(question, {coffee: p.coffee, step: 0}); 291 | }); 292 | 293 | 294 | }); 295 | 296 | function coffeeSelected(p, coffee) { 297 | p.play(`(OK|Excellent choice), let's (cook|brew|make|prepare) ${coffee} coffee`); 298 | p.play(`I can (give|tell) you brief description of ${coffee} coffee and it's ingredients, provide you with steps to (cook|brew|make|prepare) it and what equipment (is required|you will need). Please, ask.`); 299 | 300 | } 301 | 302 | function getCoffee(p) { 303 | if (p.coffee) { 304 | return Promise.resolve(p.coffee); 305 | } 306 | p.play("What coffee do you want to (make|brew|prepare)?", "What coffee do you want to (brew|make|prepare)?", "What coffee do you (like|prefer)?"); 307 | return p.then(whatCoffee); 308 | } 309 | 310 | intent(`(show|tell|what|) (me|) (how to|) ingredients`, 311 | async p => { 312 | if (!p.coffee) { 313 | p.coffee = await getCoffee(p); 314 | } 315 | getIngredients(p, p.coffee); 316 | }) 317 | 318 | 319 | intent(`(show|tell|show|explain|) (me|) (how to|) (cook|brew|make|prepare) coffee`, `(i want to|let|let's|lets) (cook|brew|make|prepare) coffee`, 320 | async p => { 321 | if (!p.coffee) { 322 | p.coffee = await getCoffee(p); 323 | } 324 | getInstructions(p, p.coffee, 0); 325 | 326 | }) 327 | 328 | intent(`(show|tell|show|explain|) (me|) (how to|) (cook|brew|make|prepare) $(T~ ${COFFEE_TYPES})`, 329 | async p => { 330 | var coffee = p.T.label; 331 | if (p.T.label === undefined) { 332 | coffee = await getCoffee(p); 333 | } 334 | coffeeSelected(p, coffee); 335 | p.then(selected, {coffee: coffee}); 336 | }) 337 | 338 | 339 | intent(`what (is|are|) (the|) (best|most popular|good) (coffee|type|recipe|)`, `what (recipe|receipt) (can|may|) (you|) (suggest|propose)`, `what (coffee|recipe) do you (prefer|advise)`, `(don't|) (know|) suggest (sth|something|)`, `(suggest|propose) (me|us|) (some|) coffee (recipe|)`, 340 | p => { 341 | var coffee = rand(Object.keys(steps)); 342 | p.play(`(I would prefer|I like|I prefer|My favorite is) ${coffee} coffee`); 343 | p.play("Would you like to (try|cook|brew|make|prepare) it?"); 344 | p.then(suggest_question, {coffee: coffee}); 345 | }) 346 | 347 | intent(`(list|tell|what) (all|possible|) (options|variants|) (of|) (coffee|) (sorts|type)`, 348 | `(tell|say|what) coffee (can|) (you|I) (cook|brew|make|prepare)`, 349 | p => { 350 | let coffeeList = Object.keys(steps).join(', '); 351 | p.play('I can (cook|brew|make|prepare) ' + coffeeList); 352 | p.play("What is your choice?", "What coffee do you want to (make|brew|prepare)?"); 353 | p.then(whatCoffee); 354 | }) 355 | 356 | intent(`How to (cook|brew|make|prepare) $(T~ ${COFFEE_TYPES})`, 357 | p => { 358 | p.play(p.T.label); 359 | let coffeeList = Object.keys(steps).join(', '); 360 | p.play('I can (cook|brew|make|prepare) ' + coffeeList); 361 | p.play("What is your choice?", "What coffee do you want to (make|brew|prepare)?"); 362 | p.then(whatCoffee); 363 | }) 364 | 365 | var begin = context(() => { 366 | 367 | follow(`(show|tell|show|explain) (me|) (how to|) (cook|brew|make|prepare) (another|) coffee`, `(i want to|let|let's|lets) (cook|brew|make|prepare) coffee`, 368 | async p => { 369 | p.coffee = await getCoffee(p); 370 | getInstructions(p, p.coffee, 0); 371 | }) 372 | }) 373 | 374 | 375 | function getInstructions(p, coffee, step) { 376 | if (coffee === undefined) { 377 | p.play("please select proper coffee type", "please check spelling", "maybe you meant something else?") 378 | p.then(whatCoffee); 379 | } 380 | var numberSteps = steps[coffee].steps.length; 381 | 382 | if (!step) { 383 | step = 0; 384 | } 385 | if (step === numberSteps - 1) { 386 | p.play("This is the final step " + (step + 1)); 387 | } else { 388 | p.play("STEP " + (step + 1)); 389 | } 390 | p.play(steps[coffee].steps[step]); 391 | } 392 | 393 | function getIngredients(p, coffee) { 394 | if (coffee === undefined) { 395 | p.play("Please select proper coffee type"); 396 | p.then(whatCoffee); 397 | } 398 | p.play(`You need following ingredients to (brew|prepare|cook|make) ${coffee} coffee`); 399 | p.play(steps[coffee].ingredients.join(', ')); 400 | } 401 | 402 | function getEquipment(p, coffee) { 403 | if (coffee === undefined) { 404 | p.play("Please select coffee type"); 405 | p.then(whatCoffee); 406 | } 407 | p.play(`You need following equipment to (brew|prepare|cook|make) ${coffee} coffee`); 408 | p.play(steps[coffee.toLowerCase()].equip.join(', ')); 409 | } 410 | 411 | function rand(list) { 412 | var index = Math.floor(Math.random() * list.length); 413 | return list[index]; 414 | } 415 | --------------------------------------------------------------------------------