Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
van100j committed Jul 11, 2017
0 parents commit f36c5d6
Show file tree
Hide file tree
Showing 11 changed files with 517 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NODE_ENV=development
PORT=4040
WSURL=http://localhost:4040
APIAI_CLIENT_TOKEN=YOUR_API_AI_TOKEN
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/client
/node_modules

*.env
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Date Bot

A simple Date bot that answers date related questions like:
- how many days between today and November 21st
- Is it Monday today
- How many days until December
- etc.

Also API.AI's small talk agent included, which enables the bot to answers questions, well... related to small talk.
13 changes: 13 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const dotenv = require('dotenv').config({
path: process.env.NODE_ENV === 'test' ? 'test.env' :
(process.env.NODE_ENV === 'production' ? 'production.env' : '.env')
});
const server = require('./server');
const app = require('./server/express');
const socket = require('./server/websocket');

server.on('request', app);

server.listen(process.env.PORT || 3000, () => {
console.info(`server/ws started on port ${process.env.PORT}`); // eslint-disable-line no-console
});
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "ws-chat-bot",
"version": "0.1.0",
"description": "Rxjs Websockets",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Vanco Stojkov <[email protected]>",
"license": "ISC",
"dependencies": {
"apiai": "^4.0.2",
"dotenv": "^4.0.0",
"express": "^4.15.3",
"moment": "^2.18.1",
"rxjs": "^5.4.2",
"uuid": "^3.1.0",
"ws": "^3.0.0"
}
}
13 changes: 13 additions & 0 deletions server/express.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const express = require('express');

const app = express();
const router = express.Router();

router.use('/*', function(req, res){
return res.sendFile('index.html', {
root: 'client/dist/'
});
});
app.use(router);

module.exports = app;
4 changes: 4 additions & 0 deletions server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const http = require('http');
const server = http.createServer();

module.exports = server;
93 changes: 93 additions & 0 deletions server/intents/date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const moment = require('moment');

const daysOfWeekByDay = {
'Sunday': 0,
'Monday': 1,
'Tuesday': 2,
'Wednesday': 3,
'Thursday': 4,
'Friday': 5,
'Saturday': 6
};

const daysOfWeekByIndex = Object.keys(daysOfWeekByDay)
.reduce((acc, d, ix) => {
acc[ix] = d;
return acc;
}, {});

const plural = (num, unit) => num + ' ' + unit + (+num !== 1 ? 's' : '');

module.exports = {
"date.between": (pars) => {
const date1 = moment(pars['date-1']);
const date2 = moment(pars['date-2']);
const unit = pars.unit;
const diff = Math.abs(date1.diff(date2, unit));

return `There are ${plural(diff, unit)} between ` + date1.format('MMMM Do YYYY') + ' and ' + date2.format('MMMM Do YYYY');
},
"date.check": (pars) => {
const { location } = pars;
const date = moment(pars.date);
const now = moment();

return now.format("MM-DD-YYYY") === date.format("MM-DD-YYYY")
? 'Yes' : 'No';
},
"date.day_of_week": (pars) => {
const { date, location } = pars;

return daysOfWeekByIndex[moment(date).format('e')];
},
"date.day_of_week.check": (pars) => {
const { dayofweek, location } = pars;
const date = moment(pars.date);
const realDayOfWeek = date.format('e');

return +daysOfWeekByDay[dayofweek] === +realDayOfWeek
? `Yes, it's ${daysOfWeekByIndex[realDayOfWeek]}`
: `No, it's ${daysOfWeekByIndex[realDayOfWeek]}`;
},
"date.get": (pars) => {
const date = pars.date ? moment(date) : moment();

return date.format('MMMM Do YYYY');
},
"date.month.check": (pars) => {
const [start, end] = pars['date-period'].split("/");
const now = moment();
const month = +moment(start).format('M');

return +now.format('M') === month
? 'Yes, it\'s ' + now.format('MMMM')
: 'No, it\'s ' + now.format('MMMM');
},
"date.month.get": () =>'It\'s ' + moment().format('MMMM'),
"date.since": (pars) => {
const { unit } = pars;
const date = moment(pars.date);
const now = moment();
const diff = Math.abs(now.diff(date, unit));

return `${plural(diff, unit)} since ` + date.format('MMMM Do YYYY');
},
"date.until": (pars) => {
const now = moment();
const date = moment(pars['date']);
const unit = pars.unit;
const diff = Math.abs(now.diff(date, unit));

return `${plural(diff, unit)} until ${date.format('MMMM Do YYYY')}`;
},
"date.year.check": (pars) => {
const [start, end] = pars['date-period'].split("/");
const now = moment();
const year = +moment(start).format('YYYY');

return +now.format('YYYY') === year
? 'Yes, it\'s ' + now.format('YYYY')
: 'No, it\'s ' + now.format('YYYY');
},
"date.year.get": () => 'The current year is ' + moment().format('YYYY'),
};
12 changes: 12 additions & 0 deletions server/intents/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const date = require('./date');

// merge all intent actions
const intents = Object.assign({}, date);

function doIntent(action, pars) {
const unrecognizedActionMsg = 'I don\t understand that. Can you try again?';

return intents[action] ? intents[action](pars) : unrecognizedActionMsg;
}

module.exports = doIntent;
56 changes: 56 additions & 0 deletions server/websocket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const server = require('./');
const doIntent = require('./intents');
const WebSocket = require('ws');
const apiai = require("apiai")(process.env.APIAI_CLIENT_TOKEN);
const uuidv4 = require('uuid/v4');
const util = require('util')

const wss = new WebSocket.Server({server: server});

wss.on('connection', (ws) => {
ws.on('message', (msg) => {
try {
const input = JSON.parse(msg);

if (input.type === 'user' && input.msg) {
const sessionId = input.sessionId || uuidv4();

callApiAi(input.msg, sessionId)
.then((response) => {
const { parameters, action, fulfillment } = response.result;
const output = doIntent(action, parameters);

return ws.send(JSON.stringify({type: 'bot', msg: output}));
})
.catch(error =>
ws.send(JSON.stringify({type: 'bot', msg: 'Didn\'t quite get that?'}))
);
}
} catch (err) {
// TODO: handle JSON.parse error
ws.send(JSON.stringify({type: 'bot', msg: 'Didn\'t quite get that?'}))
}
});

// TODO: generate ws session ids w/ uuid or sth.
ws.send(JSON.stringify({type: 'sessionId', msg: uuidv4()}));
// ws.send(JSON.stringify({type: 'bot', msg: 'Hi there!'}));
ws.send(JSON.stringify({type: 'bot', msg: '⏰ I\'m a date bot — ask me something about date!'}));
});

function callApiAi(text, sessionId) {
return new Promise((resolve, reject) => {
try {
// TODO: generate ws session ids w/ uuid or sth.
const request = apiai.textRequest(text, { sessionId: sessionId });

request.on('response', response => resolve(response));
request.on('error', error => reject(error));
request.end();
} catch (err) {
reject(err);
}
});
}

module.exports = wss;
Loading

0 comments on commit f36c5d6

Please sign in to comment.