Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Cleanup and modularization for separation of concerns #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,66 @@ utils/tmp/*
TODO
.idea
.vscode/

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next

7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const server = require("./server");

const PORT = process.env.PORT || 5000;

server.listen(PORT, () => {
console.log(`\n::: Listening on port ${PORT} :::\n`);
});
159 changes: 159 additions & 0 deletions middleware/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/* Middleware functions */
module.exports = {
cleanupTextMiddleWare,
stopMiddleware,
yesNoMiddleware,
deleteMiddleware,
currentRequestMiddleware,
caseIdMiddleware
};

/**
* Strips line feeds, returns, and emojis from string and trims it
*
* @param {String} text incoming message to evaluate
* @return {String} cleaned up string
*/
function cleanupTextMiddleWare(req, res, next) {
let text = req.body.Body.replace(/[\r\n|\n].*/g, "");
req.body.Body = emojiStrip(text)
.trim()
.toUpperCase();
next();
}

/**
* Checks for 'STOP' text. We will recieve this if the user requests that twilio stop sending texts
* All further attempts to send a text (inlcuding responing to this text) will fail until the user restores this.
* This will delete any requests the user currently has (alternatively we could mark them inactive and reactiveate if they restart)
*/
function stopMiddleware(req, res, next) {
const stop_words = [
"STOP",
"STOPALL",
"UNSUBSCRIBE",
"CANCEL",
"END",
"QUIT"
];
const text = req.body.Body;
if (!stop_words.includes(text)) return next();

db.deactivateRequestsFor(req.body.From)
.then(case_ids => {
res[action_symbol] = "stop";
return res.sendStatus(200); // once stopped replies don't make it to the user
})
.catch(err => next(err));
}

/**
* Handles cases when user has send a yes or no text.
*/
function yesNoMiddleware(req, res, next) {
// Yes or No resonses are only meaningful if we also know the citation ID.
if (!req.session.case_id) return next();

const twiml = new MessagingResponse();
if (isResponseYes(req.body.Body)) {
db.addRequest({
case_id: req.session.case_id,
phone: req.body.From,
known_case: req.session.known_case
})
.then(() => {
twiml.message(
req.session.known_case
? messages.weWillRemindYou()
: messages.weWillKeepLooking()
);
res[action_symbol] = req.session.known_case
? "schedule_reminder"
: "schedule_unmatched";
req.session = null;
req.session = null;
res.send(twiml.toString());
})
.catch(err => next(err));
} else if (isResponseNo(req.body.Body)) {
res[action_symbol] = "decline_reminder";
twiml.message(
req.session.known_case
? messages.repliedNo()
: messages.repliedNoToKeepChecking()
);
req.session = null;
res.send(twiml.toString());
} else {
next();
}
}

/**
* Handles cases where user has entered a case they are already subscribed to
* and then type Delete
*/
function deleteMiddleware(req, res, next) {
// Delete response is only meaningful if we have a delete_case_id.
const case_id = req.session.delete_case_id;
const phone = req.body.From;
if (!case_id || req.body.Body !== "DELETE") return next();
res[action_symbol] = "delete_request";
const twiml = new MessagingResponse();
db.deactivateRequest(case_id, phone)
.then(() => {
req.session = null;
twiml.message(messages.weWillStopSending(case_id));
res.send(twiml.toString());
})
.catch(err => next(err));
}

/**
* Responds if the sending phone number is alreay subscribed to this case_id=
*/
function currentRequestMiddleware(req, res, next) {
const text = req.body.Body;
const phone = req.body.From;
if (!possibleCaseID(text)) return next();
db.findRequest(text, phone)
.then(results => {
if (!results || results.length === 0) return next();

const twiml = new MessagingResponse();
// looks like they're already subscribed
res[action_symbol] = "already_subscribed";
req.session.delete_case_id = text;
twiml.message(messages.alreadySubscribed(text));
res.send(twiml.toString());
})
.catch(err => next(err));
}

/**
* If input looks like a case number handle it
*/
function caseIdMiddleware(req, res, next) {
const text = req.body.Body;
if (!possibleCaseID(text)) return next();
const twiml = new MessagingResponse();

db.findCitation(req.body.Body)
.then(results => {
if (!results || results.length === 0) {
// Looks like it could be a citation that we don't know about yet
res[action_symbol] = "unmatched_case";
twiml.message(messages.notFoundAskToKeepLooking());
req.session.known_case = false;
req.session.case_id = text;
} else {
// They sent a known citation!
res[action_symbol] = "found_case";
twiml.message(messages.foundItAskForReminder(results[0]));
req.session.case_id = text;
req.session.known_case = true;
}
res.send(twiml.toString());
})
.catch(err => next(err));
}
12 changes: 10 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"main": "web.js",
"scripts": {
"test": "NODE_ENV=test mocha --exit test",
"start": "node web.js",
"start": "node index.js",
"dbsetup": "node utils/createTables.js",
"loaddata": "node runners/load.js"
},
Expand All @@ -21,6 +21,7 @@
"dependencies": {
"body-parser": "^1.18.3",
"cookie-session": "^2.0.0-beta.3",
"cors": "^2.8.5",
"csv": "^1.2.1",
"dotenv": "~4.0.0",
"emoji-strip": "^1.0.1",
Expand Down Expand Up @@ -49,4 +50,4 @@
"supertest": "~3.0.0",
"supertest-session": "^3.3.0"
}
}
}
Loading